Add live field provenance for React components

This commit is contained in:
Timothy J. Aveni
2026-07-12 18:24:18 -07:00
parent 601728c139
commit 4c59e6da0f
3 changed files with 343 additions and 29 deletions
+80
View File
@@ -15,11 +15,13 @@ import {
import {
CaminoService,
CrdtValueSchema,
FieldValueSourceSchema,
ListValueSchema,
NullValueSchema,
ObjectValueSchema,
RefValueSchema,
ValueSchema,
ValueSourceSchema,
type Value,
} from "./camino/api_pb.js";
import {
@@ -181,6 +183,44 @@ export type Field<T> = {
set(value: T): Promise<void>;
};
export type LiveFieldValue<T> = {
$caminoValue: T;
$caminoSource: {
field: {
objectId: string;
fieldName: string;
fieldType?: string;
fieldStorage?: string;
conflictStrategy?: string;
revision?: number | string | bigint;
};
};
};
export const liveFieldValue = <T>(params: {
value: T;
objectId: string;
fieldName: string;
fieldType?: string;
fieldStorage?: string;
conflictStrategy?: string;
revision?: number | string | bigint;
}): LiveFieldValue<T> => ({
$caminoValue: params.value,
$caminoSource: {
field: {
objectId: params.objectId,
fieldName: params.fieldName,
...(params.fieldType ? { fieldType: params.fieldType } : {}),
...(params.fieldStorage ? { fieldStorage: params.fieldStorage } : {}),
...(params.conflictStrategy
? { conflictStrategy: params.conflictStrategy }
: {}),
...(params.revision !== undefined ? { revision: params.revision } : {}),
},
},
});
export type RuntimeHandler<Context> = (
context: Context,
) => Promise<unknown> | unknown;
@@ -429,6 +469,43 @@ const bytesToBase64 = (value: Uint8Array) =>
const base64ToBytes = (value: string) => Buffer.from(value, "base64");
const sourceToProto = (value: unknown) => {
if (!isRecord(value) || !isRecord(value.field)) {
return undefined;
}
const field = value.field;
if (
typeof field.objectId !== "string" ||
typeof field.fieldName !== "string"
) {
return undefined;
}
return create(ValueSourceSchema, {
field: create(FieldValueSourceSchema, {
objectId: field.objectId,
fieldName: field.fieldName,
fieldType: typeof field.fieldType === "string" ? field.fieldType : "",
fieldStorage:
typeof field.fieldStorage === "string" ? field.fieldStorage : "",
conflictStrategy:
typeof field.conflictStrategy === "string" ? field.conflictStrategy : "",
revision:
typeof field.revision === "bigint"
? field.revision
: typeof field.revision === "number"
? BigInt(field.revision)
: typeof field.revision === "string" && /^\d+$/.test(field.revision)
? BigInt(field.revision)
: 0n,
}),
});
};
const withSource = (value: Value, source: unknown): Value => {
const sourceValue = sourceToProto(source);
return sourceValue ? create(ValueSchema, { ...value, source: sourceValue }) : value;
};
export const jsToProtoValue = (value: unknown): Value => {
if (value === null || value === undefined) {
return create(ValueSchema, {
@@ -474,6 +551,9 @@ export const jsToProtoValue = (value: unknown): Value => {
});
}
if (isRecord(value)) {
if (Object.hasOwn(value, "$caminoValue") && isRecord(value.$caminoSource)) {
return withSource(jsToProtoValue(value.$caminoValue), value.$caminoSource);
}
if (typeof value.$caminoRef === "string") {
return create(ValueSchema, {
kind: {