diff --git a/src/index.ts b/src/index.ts index d78d4e6..588fb5f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -414,6 +414,12 @@ export type RuntimeFunctionSpec = { exportName: ExportName; symbol: string; operation?: string; + field?: { + name: string; + type?: string; + storage?: string; + conflict?: string; + }; }; class AsyncEventQueue implements AsyncIterableIterator { @@ -698,10 +704,43 @@ const derivedDependencyToProto = (dependency: DerivedDependency) => sourceField: dependency.kind === "edges" ? dependency.sourceField ?? "" : "", }); -const derivedEmissionToProto = (event: DerivedWatchEmission): WatchEvent => +const fieldSourceForSpec = ( + spec: RuntimeFunctionSpec, + objectId: string, +): LiveFieldValue["$caminoSource"] | undefined => { + if (!spec.field) { + return undefined; + } + return { + field: { + objectId, + fieldName: spec.field.name, + ...(spec.field.type ? { fieldType: spec.field.type } : {}), + ...(spec.field.storage ? { fieldStorage: spec.field.storage } : {}), + ...(spec.field.conflict ? { conflictStrategy: spec.field.conflict } : {}), + }, + }; +}; + +const wrapFieldResult = ( + spec: RuntimeFunctionSpec, + objectId: string, + value: unknown, +) => { + const source = fieldSourceForSpec(spec, objectId); + return source ? { $caminoValue: value, $caminoSource: source } : value; +}; + +const derivedEmissionToProtoForSpec = ( + spec: RuntimeFunctionSpec, + objectId: string, + event: DerivedWatchEmission, +): WatchEvent => create(WatchEventSchema, { watchId: event.watchId, - value: jsToProtoValue(event.value ?? null), + value: jsToProtoValue( + event.error ? null : wrapFieldResult(spec, objectId, event.value ?? null), + ), dependencies: event.dependencies.map(derivedDependencyToProto), error: event.error ?? "", initial: event.initial, @@ -895,11 +934,16 @@ export const serveQuixosPackageRuntime = < async invoke(request) { try { - const { runtimeFunction } = resolveRuntimeFunction(request.function); + const { spec, runtimeFunction } = resolveRuntimeFunction(request.function); + const value = await runtimeFunction( + params.createContext(trackingCamino, request), + ); return create(InvokeResponseSchema, { ok: true, result: jsToProtoValue( - await runtimeFunction(params.createContext(trackingCamino, request)), + spec.operation === "get" + ? wrapFieldResult(spec, request.objectId, value) + : value, ), }); } catch (error) { @@ -929,7 +973,9 @@ export const serveQuixosPackageRuntime = < } const sink: DerivedWatchSink = { publish(event) { - queue.push(derivedEmissionToProto(event)); + queue.push( + derivedEmissionToProtoForSpec(spec, request.objectId, event), + ); }, }; const result = await watchSinkStorage.run(sink, async () =>