Skip to content

Commit 4dd8861

Browse files
committed
sdks/ts: replace any with proper types in SDK core
Replace `any` types with `unknown`, generics, and proper type constraints across the SDK. Uses Reflect.construct for safe instantiation, Object.assign for typed function objects, and generic wrappers for deserialize functions.
1 parent 4ecb6f9 commit 4dd8861

File tree

14 files changed

+149
-117
lines changed

14 files changed

+149
-117
lines changed

sdks/ts/packages/golem-ts-sdk/src/baseAgent.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,11 @@ export class BaseAgent {
102102
*/
103103
async loadSnapshot(bytes: Uint8Array): Promise<void> {
104104
const text = new TextDecoder().decode(bytes);
105-
const state = JSON.parse(text);
105+
const state = JSON.parse(text) as Partial<this>;
106106

107107
for (const [k, v] of Object.entries(state)) {
108108
if (k === 'cachedAgentType' || k === 'agentClassName') continue;
109-
(this as any)[k] = v;
109+
this[k as keyof this] = v;
110110
}
111111
}
112112

@@ -220,15 +220,17 @@ export class BaseAgent {
220220
*
221221
* ```
222222
*/
223+
type MethodKeys<T> = {
224+
[K in keyof T]-?: T[K] extends (...args: never[]) => unknown ? K : never;
225+
}[keyof T];
226+
223227
export type Client<T> = {
224-
[K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: T[K] extends (
225-
...args: infer A
226-
) => infer R
228+
[K in MethodKeys<T>]: T[K] extends (...args: infer A) => infer R
227229
? RemoteMethod<GetArgs<A>, Awaited<R>>
228230
: never;
229231
};
230232

231-
export type RemoteMethod<Args extends any[], R> = {
233+
export type RemoteMethod<Args extends unknown[], R> = {
232234
(...args: Args): Promise<R>;
233235
trigger: (...args: Args) => void;
234236
schedule: (ts: Datetime, ...args: Args) => void;
@@ -254,8 +256,8 @@ type GetArgs<T extends readonly unknown[]> =
254256
type IsOptional<T extends readonly unknown[], K extends keyof T> =
255257
{} extends Pick<T, K> ? true : false;
256258

257-
type AllOptional<T extends readonly unknown[], I extends any[] = []> = T extends readonly [
258-
any,
259+
type AllOptional<T extends readonly unknown[], I extends unknown[] = []> = T extends readonly [
260+
unknown,
259261
...infer R,
260262
]
261263
? IsOptional<T, I['length']> extends true

sdks/ts/packages/golem-ts-sdk/src/decorators/agent.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ export type AgentDecoratorOptions = {
6060
phantom?: boolean;
6161
};
6262

63+
type MutableAgentStatics = {
64+
get?: unknown;
65+
newPhantom?: unknown;
66+
getPhantom?: unknown;
67+
};
68+
6369
function parseDurationToNanoseconds(duration: string): bigint {
6470
const milliseconds = ms(duration as ms.StringValue);
6571
if (milliseconds === undefined) {
@@ -325,10 +331,10 @@ export function agent(options?: AgentDecoratorOptions) {
325331
);
326332
}
327333

328-
(ctor as any).get = getRemoteClient(agentClassName, agentType, ctor);
329-
(ctor as any).newPhantom = getNewPhantomRemoteClient(agentClassName, agentType, ctor);
330-
331-
(ctor as any).getPhantom = getPhantomRemoteClient(agentClassName, agentType, ctor);
334+
const decoratedCtor = ctor as T & MutableAgentStatics;
335+
decoratedCtor.get = getRemoteClient(agentClassName, agentType, ctor);
336+
decoratedCtor.newPhantom = getNewPhantomRemoteClient(agentClassName, agentType, ctor);
337+
decoratedCtor.getPhantom = getPhantomRemoteClient(agentClassName, agentType, ctor);
332338

333339
AgentInitiatorRegistry.register(agentTypeName, {
334340
initiate: (constructorInput: DataValue, principal: Principal) => {
@@ -349,7 +355,7 @@ export function agent(options?: AgentDecoratorOptions) {
349355
};
350356
}
351357

352-
const instance = new ctor(...deserializedConstructorArgs.val);
358+
const instance = Reflect.construct(ctor, deserializedConstructorArgs.val) as BaseAgent;
353359

354360
const agentId = getRawSelfAgentId();
355361
if (!agentId.value.startsWith(agentTypeName.value)) {

sdks/ts/packages/golem-ts-sdk/src/host/result.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,25 +316,25 @@ function flatten<T, E, E2>(this: Result<Result<T, E>, E2>): Result<T, E | E2> {
316316
else return this.val;
317317
}
318318

319-
function assertErrorInstanceOf<T, C extends abstract new (..._: any) => any>(
319+
function assertErrorInstanceOf<T, C extends abstract new (..._: unknown[]) => unknown>(
320320
this: Result.Ok<T>,
321321
constructor: C,
322322
): Result.Ok<T>;
323-
function assertErrorInstanceOf<E, C extends abstract new (..._: any) => any>(
323+
function assertErrorInstanceOf<E, C extends abstract new (..._: unknown[]) => unknown>(
324324
this: Result.Err<E>,
325325
constructor: C,
326326
): Result.Err<E & InstanceType<C>>;
327-
function assertErrorInstanceOf<T, E, C extends abstract new (..._: any) => any>(
327+
function assertErrorInstanceOf<T, E, C extends abstract new (..._: unknown[]) => unknown>(
328328
this: Result<T, E>,
329329
constructor: C,
330330
): Result<T, E & InstanceType<C>>;
331-
function assertErrorInstanceOf<T, E, C extends abstract new (..._: any) => any>(
331+
function assertErrorInstanceOf<T, E, C extends abstract new (..._: unknown[]) => unknown>(
332332
this: Result<T, E>,
333333
constructor: C,
334334
): Result<T, E & InstanceType<C>> {
335335
if (this.isOk()) return this;
336336

337-
if (this.val instanceof constructor) return this as any;
337+
if (this.val instanceof constructor) return this as Result<T, E & InstanceType<C>>;
338338

339339
throw new TypeError(`Assertion failed: Expected error to be an instance of ${constructor.name}.`);
340340
}

sdks/ts/packages/golem-ts-sdk/src/host/transaction.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,6 @@ export function fallibleTransaction<Out, Err>(
233233
* ```
234234
*
235235
*/
236-
export type OperationErrors<T extends Operation<any, any, any>[]> = {
237-
[K in keyof T]: T[K] extends Operation<any, any, infer Err> ? Err : never;
236+
export type OperationErrors<T extends Operation<unknown, unknown, unknown>[]> = {
237+
[K in keyof T]: T[K] extends Operation<unknown, unknown, infer Err> ? Err : never;
238238
}[number];

sdks/ts/packages/golem-ts-sdk/src/internal/clientGeneration.ts

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
ElementValue,
3232
TextReference,
3333
} from 'golem:agent/common@1.5.0';
34-
import { RemoteMethod } from '../baseAgent';
34+
import { BaseAgent, RemoteMethod } from '../baseAgent';
3535
import { AgentMethodParamRegistry } from './registry/agentMethodParamRegistry';
3636
import { AgentConstructorParamRegistry } from './registry/agentConstructorParamRegistry';
3737
import { AgentMethodRegistry } from './registry/agentMethodRegistry';
@@ -40,19 +40,20 @@ import {
4040
serializeTsValueToTextReference,
4141
} from './mapping/values/serializer';
4242
import { TypeInfoInternal } from './typeInfoInternal';
43-
import {
44-
deserializeDataValue,
45-
ParameterDetail,
46-
serializeToDataValue,
47-
} from './mapping/values/dataValue';
43+
import { deserializeDataValue, serializeToDataValue } from './mapping/values/dataValue';
4844
import { randomUuid } from '../host/hostapi';
4945
import { AgentId } from '../agentId';
5046
import * as util from 'node:util';
5147

52-
export function getRemoteClient<T extends new (...args: any[]) => any>(
48+
type AgentClass<T extends BaseAgent = BaseAgent> = {
49+
readonly name: string;
50+
readonly prototype: T;
51+
};
52+
53+
export function getRemoteClient(
5354
agentClassName: AgentClassName,
5455
agentType: AgentType,
55-
ctor: T,
56+
ctor: AgentClass,
5657
) {
5758
const metadata = TypeMetadata.get(ctor.name);
5859

@@ -63,7 +64,7 @@ export function getRemoteClient<T extends new (...args: any[]) => any>(
6364
}
6465
const shared = new WasmRpxProxyHandlerShared(metadata, agentClassName, agentType);
6566

66-
return (...args: any[]) => {
67+
return (...args: unknown[]) => {
6768
const instance = Object.create(ctor.prototype);
6869

6970
const constructedId = shared.constructAgentId(args);
@@ -72,10 +73,10 @@ export function getRemoteClient<T extends new (...args: any[]) => any>(
7273
};
7374
}
7475

75-
export function getPhantomRemoteClient<T extends new (phantomId: Uuid, ...args: any[]) => any>(
76+
export function getPhantomRemoteClient(
7677
agentClassName: AgentClassName,
7778
agentType: AgentType,
78-
ctor: T,
79+
ctor: AgentClass,
7980
) {
8081
const metadata = TypeMetadata.get(ctor.name);
8182

@@ -87,7 +88,7 @@ export function getPhantomRemoteClient<T extends new (phantomId: Uuid, ...args:
8788

8889
const shared = new WasmRpxProxyHandlerShared(metadata, agentClassName, agentType);
8990

90-
return (finalPhantomId: Uuid, ...args: any[]) => {
91+
return (finalPhantomId: Uuid, ...args: unknown[]) => {
9192
const instance = Object.create(ctor.prototype);
9293

9394
const constructedId = shared.constructAgentId(args, finalPhantomId);
@@ -96,10 +97,10 @@ export function getPhantomRemoteClient<T extends new (phantomId: Uuid, ...args:
9697
};
9798
}
9899

99-
export function getNewPhantomRemoteClient<T extends new (...args: any[]) => any>(
100+
export function getNewPhantomRemoteClient(
100101
agentClassName: AgentClassName,
101102
agentType: AgentType,
102-
ctor: T,
103+
ctor: AgentClass,
103104
) {
104105
const metadata = TypeMetadata.get(ctor.name);
105106

@@ -110,7 +111,7 @@ export function getNewPhantomRemoteClient<T extends new (...args: any[]) => any>
110111
}
111112
const shared = new WasmRpxProxyHandlerShared(metadata, agentClassName, agentType);
112113

113-
return (...args: any[]) => {
114+
return (...args: unknown[]) => {
114115
const instance = Object.create(ctor.prototype);
115116

116117
const finalPhantomId = randomUuid();
@@ -166,7 +167,7 @@ class WasmRpxProxyHandlerShared {
166167
}
167168
}
168169

169-
constructAgentId(args: any[], phantomId?: Uuid): ConstructedAgentId {
170+
constructAgentId(args: unknown[], phantomId?: Uuid): ConstructedAgentId {
170171
let constructorDataValue: DataValue;
171172

172173
if (args.length === 1 && this.constructorParamTypes[0].tag === 'multimodal') {
@@ -289,12 +290,12 @@ class WasmRpxProxyHandlerShared {
289290
}
290291
}
291292

292-
class WasmRpcProxyHandler implements ProxyHandler<any> {
293+
class WasmRpcProxyHandler implements ProxyHandler<Record<string, unknown>> {
293294
private readonly shared: WasmRpxProxyHandlerShared;
294295
private readonly agentId: AgentId;
295296
private readonly wasmRpc: WasmRpc;
296297

297-
private readonly methodProxyCache = new Map<string, RemoteMethod<any[], any>>();
298+
private readonly methodProxyCache = new Map<string, RemoteMethod<unknown[], unknown>>();
298299

299300
private readonly getIdMethod: () => AgentId = () => this.agentId;
300301
private readonly phantomIdMethod: () => Uuid | undefined = () => {
@@ -314,8 +315,8 @@ class WasmRpcProxyHandler implements ProxyHandler<any> {
314315
);
315316
}
316317

317-
get(target: any, prop: string | symbol) {
318-
const val = target[prop];
318+
get(target: Record<string, unknown>, prop: string | symbol) {
319+
const val = target[prop.toString()];
319320
const propString = prop.toString();
320321

321322
if (typeof val === 'function') {
@@ -349,12 +350,12 @@ class WasmRpcProxyHandler implements ProxyHandler<any> {
349350
return undefined;
350351
}
351352

352-
private createMethodProxy(prop: string): RemoteMethod<any[], any> {
353+
private createMethodProxy(prop: string): RemoteMethod<unknown[], unknown> {
353354
const methodInfo = this.shared.getMethodInfo(prop);
354355
const agentIdString = this.agentId.value;
355356
const wasmRpc = this.wasmRpc;
356357

357-
async function invokeAndAwait(...fnArgs: any[]) {
358+
async function invokeAndAwait(...fnArgs: unknown[]) {
358359
const inputDataValue = serializeArgs(methodInfo.params, fnArgs);
359360

360361
const rpcResultFuture = wasmRpc.asyncInvokeAndAwait(methodInfo.name, inputDataValue);
@@ -383,26 +384,29 @@ class WasmRpcProxyHandler implements ProxyHandler<any> {
383384
return deserializeRpcResult(resultDataValue, methodInfo.returnType);
384385
}
385386

386-
function invokeFireAndForget(...fnArgs: any[]) {
387+
function invokeFireAndForget(...fnArgs: unknown[]) {
387388
const inputDataValue = serializeArgs(methodInfo.params, fnArgs);
388389
wasmRpc.invoke(methodInfo.name, inputDataValue);
389390
}
390391

391-
function invokeSchedule(ts: Datetime, ...fnArgs: any[]) {
392+
function invokeSchedule(ts: Datetime, ...fnArgs: unknown[]) {
392393
const inputDataValue = serializeArgs(methodInfo.params, fnArgs);
393394
wasmRpc.scheduleInvocation(ts, methodInfo.name, inputDataValue);
394395
}
395396

396-
const methodFn: any = (...args: any[]) => invokeAndAwait(...args);
397-
398-
methodFn.trigger = (...args: any[]) => invokeFireAndForget(...args);
399-
methodFn.schedule = (ts: Datetime, ...args: any[]) => invokeSchedule(ts, ...args);
397+
const methodFn: RemoteMethod<unknown[], unknown> = Object.assign(
398+
(...args: unknown[]) => invokeAndAwait(...args),
399+
{
400+
trigger: (...args: unknown[]) => invokeFireAndForget(...args),
401+
schedule: (ts: Datetime, ...args: unknown[]) => invokeSchedule(ts, ...args),
402+
},
403+
);
400404

401-
return methodFn as RemoteMethod<any[], any>;
405+
return methodFn;
402406
}
403407
}
404408

405-
function serializeArgs(params: CachedParamInfo[], fnArgs: any[]): DataValue {
409+
function serializeArgs(params: CachedParamInfo[], fnArgs: unknown[]): DataValue {
406410
const elementValues: ElementValue[] = [];
407411
for (const [index, fnArg] of fnArgs.entries()) {
408412
const param = params[index];
@@ -441,14 +445,8 @@ function serializeArgs(params: CachedParamInfo[], fnArgs: any[]): DataValue {
441445
`Failed to serialize multimodal arg ${param.name}: ${dataValueEither.val}`,
442446
);
443447
}
444-
// For a multimodal param, the serialized DataValue is itself the result;
445-
// we wrap it as a single tuple with the multimodal elements
446448
const multimodalDv = dataValueEither.val;
447449
if (multimodalDv.tag === 'multimodal') {
448-
// Each multimodal element becomes part of the overall DataValue
449-
// But since params are tuple-based, we need to wrap multimodal as a single element
450-
// The server expects each param as an ElementValue in the tuple
451-
// For multimodal, we serialize the whole thing as a component-model WitValue
452450
for (const [, ev] of multimodalDv.val) {
453451
elementValues.push(ev);
454452
}
@@ -464,7 +462,10 @@ function serializeArgs(params: CachedParamInfo[], fnArgs: any[]): DataValue {
464462
return { tag: 'tuple', val: elementValues };
465463
}
466464

467-
function deserializeRpcResult(resultDataValue: DataValue, typeInfoInternal: TypeInfoInternal): any {
465+
function deserializeRpcResult<T = unknown>(
466+
resultDataValue: DataValue,
467+
typeInfoInternal: TypeInfoInternal,
468+
): T {
468469
return Either.getOrThrowWith(
469470
deserializeDataValue(
470471
resultDataValue,
@@ -477,5 +478,5 @@ function deserializeRpcResult(resultDataValue: DataValue, typeInfoInternal: Type
477478
{ tag: 'anonymous' },
478479
),
479480
(err) => new Error(`Failed to deserialize return value of RPC call: ${err}`),
480-
)[0];
481+
)[0] as T;
481482
}

sdks/ts/packages/golem-ts-sdk/src/internal/mapping/types/handlers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const handlers: { [K in TsType['kind']]: Handler<K> } = {
7070
config: unsupported('Config'),
7171
};
7272

73-
function unsupported(kind: string): Handler<any> {
73+
function unsupported<K extends TsType['kind']>(kind: string): Handler<K> {
7474
return ({ scopeName, parameterInScope }) =>
7575
Either.left(
7676
`Unsupported type \`${kind}\`` +
@@ -79,7 +79,7 @@ function unsupported(kind: string): Handler<any> {
7979
);
8080
}
8181

82-
function unsupportedWithHint(kind: string, hint: string): Handler<any> {
82+
function unsupportedWithHint<K extends TsType['kind']>(kind: string, hint: string): Handler<K> {
8383
return ({ scopeName, parameterInScope }) =>
8484
Either.left(
8585
`Unsupported type \`${kind}\`${scopeName ? ` in ${scopeName}` : ''}` +

sdks/ts/packages/golem-ts-sdk/src/internal/mapping/types/witTypeBuilder.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export class WitTypeBuilder {
4949
}
5050

5151
private convert(typ: AnalysedType): WitTypeNode {
52+
let kind = typ.kind;
5253
switch (typ.kind) {
5354
case 'variant': {
5455
const cases: [string, NodeIndex | undefined][] = typ.value.cases.map(
@@ -127,7 +128,7 @@ export class WitTypeBuilder {
127128
}
128129

129130
default:
130-
throw new Error(`Unhandled AnalysedType kind: ${(typ as any).kind}`);
131+
throw new Error(`Unhandled AnalysedType kind: ${kind}`);
131132
}
132133
}
133134
}

0 commit comments

Comments
 (0)