Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a83c43f9d9 | |||
| 8fc1060b65 | |||
| cad6057316 | |||
| b1d0667045 | |||
| 1df06f4be7 | |||
| 9674ffa108 | |||
| bd684cd07d | |||
| 02e2313aa9 | |||
| 765c321f8a | |||
| df4946e016 | |||
| fa6939d40c | |||
| d2e3172a62 | |||
| 0f2e0f6d89 | |||
| 415eb6b411 | |||
| 5ab1ec5428 | |||
| 85e0dafbeb | |||
| 9c7614cac1 | |||
| b4ab4c49c9 | |||
| 0fb0da6b18 | |||
| 955350b487 | |||
| 337036e996 | |||
| 740bbc7aff | |||
| 4c042e571c | |||
| b7cf9eaa07 | |||
| 19b101f348 | |||
| 7134b30ba3 | |||
| 88f11767b1 | |||
| 79e602278c | |||
| dd5e70514c | |||
| 2a67a2c3f5 | |||
| 79bc6abca5 | |||
| d7cc562bf6 | |||
| 97e91221d7 | |||
| 2bf0e44646 | |||
| 056b3eaa8e | |||
| 65eb332d88 | |||
| a61e3e546c | |||
| 4c59e6da0f | |||
| 4424720987 | |||
| 601728c139 |
@@ -0,0 +1 @@
|
|||||||
|
.yarn/install-state.gz
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git",
|
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git",
|
||||||
"sourceCommit": "e0d8da526c065f66ece8a8230a31be4168ccddcc",
|
"sourceCommit": "2acad11c4bcae2bbde2180327d45d5db9cffc9ed",
|
||||||
"sourcePath": "quixos-instance/packages/camino-package-runtime",
|
"sourcePath": "quixos-instance/packages/camino-package-runtime",
|
||||||
"exportName": "camino-package-runtime",
|
"exportName": "camino-package-runtime",
|
||||||
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git"
|
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git"
|
||||||
|
|||||||
Binary file not shown.
Vendored
+907
@@ -0,0 +1,907 @@
|
|||||||
|
import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2";
|
||||||
|
import type { ClassSchema } from "./schema_pb.js";
|
||||||
|
import type { FunctionRef } from "../quixos/refs_pb.js";
|
||||||
|
import type { Message } from "@bufbuild/protobuf";
|
||||||
|
/**
|
||||||
|
* Describes the file camino/api.proto.
|
||||||
|
*/
|
||||||
|
export declare const file_camino_api: GenFile;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.RegisterSchemaRequest
|
||||||
|
*/
|
||||||
|
export type RegisterSchemaRequest = Message<"camino.RegisterSchemaRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string source_file = 1;
|
||||||
|
*/
|
||||||
|
sourceFile: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.RegisterSchemaRequest.
|
||||||
|
* Use `create(RegisterSchemaRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const RegisterSchemaRequestSchema: GenMessage<RegisterSchemaRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.RegisterSchemaResponse
|
||||||
|
*/
|
||||||
|
export type RegisterSchemaResponse = Message<"camino.RegisterSchemaResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated string class_ids = 1;
|
||||||
|
*/
|
||||||
|
classIds: string[];
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.ClassSchema classes = 2;
|
||||||
|
*/
|
||||||
|
classes: ClassSchema[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.RegisterSchemaResponse.
|
||||||
|
* Use `create(RegisterSchemaResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const RegisterSchemaResponseSchema: GenMessage<RegisterSchemaResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListClassesRequest
|
||||||
|
*/
|
||||||
|
export type ListClassesRequest = Message<"camino.ListClassesRequest"> & {};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListClassesRequest.
|
||||||
|
* Use `create(ListClassesRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListClassesRequestSchema: GenMessage<ListClassesRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.RegisteredClass
|
||||||
|
*/
|
||||||
|
export type RegisteredClass = Message<"camino.RegisteredClass"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string id = 1;
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.ClassSchema schema = 2;
|
||||||
|
*/
|
||||||
|
schema?: ClassSchema | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.RegisteredClass.
|
||||||
|
* Use `create(RegisteredClassSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const RegisteredClassSchema: GenMessage<RegisteredClass>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListClassesResponse
|
||||||
|
*/
|
||||||
|
export type ListClassesResponse = Message<"camino.ListClassesResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.RegisteredClass classes = 1;
|
||||||
|
*/
|
||||||
|
classes: RegisteredClass[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListClassesResponse.
|
||||||
|
* Use `create(ListClassesResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListClassesResponseSchema: GenMessage<ListClassesResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.NullValue
|
||||||
|
*/
|
||||||
|
export type NullValue = Message<"camino.NullValue"> & {};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.NullValue.
|
||||||
|
* Use `create(NullValueSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const NullValueSchema: GenMessage<NullValue>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ObjectValue
|
||||||
|
*/
|
||||||
|
export type ObjectValue = Message<"camino.ObjectValue"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, camino.Value> fields = 1;
|
||||||
|
*/
|
||||||
|
fields: {
|
||||||
|
[key: string]: Value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ObjectValue.
|
||||||
|
* Use `create(ObjectValueSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ObjectValueSchema: GenMessage<ObjectValue>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListValue
|
||||||
|
*/
|
||||||
|
export type ListValue = Message<"camino.ListValue"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.Value values = 1;
|
||||||
|
*/
|
||||||
|
values: Value[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListValue.
|
||||||
|
* Use `create(ListValueSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListValueSchema: GenMessage<ListValue>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.RefValue
|
||||||
|
*/
|
||||||
|
export type RefValue = Message<"camino.RefValue"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.RefValue.
|
||||||
|
* Use `create(RefValueSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const RefValueSchema: GenMessage<RefValue>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CrdtValue
|
||||||
|
*/
|
||||||
|
export type CrdtValue = Message<"camino.CrdtValue"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string type = 1;
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string encoding = 2;
|
||||||
|
*/
|
||||||
|
encoding: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: bytes payload = 3;
|
||||||
|
*/
|
||||||
|
payload: Uint8Array;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CrdtValue.
|
||||||
|
* Use `create(CrdtValueSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CrdtValueSchema: GenMessage<CrdtValue>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.FieldValueSource
|
||||||
|
*/
|
||||||
|
export type FieldValueSource = Message<"camino.FieldValueSource"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string field_name = 2;
|
||||||
|
*/
|
||||||
|
fieldName: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string field_type = 3;
|
||||||
|
*/
|
||||||
|
fieldType: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string field_storage = 4;
|
||||||
|
*/
|
||||||
|
fieldStorage: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string conflict_strategy = 5;
|
||||||
|
*/
|
||||||
|
conflictStrategy: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: uint64 revision = 6;
|
||||||
|
*/
|
||||||
|
revision: bigint;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.FieldValueSource.
|
||||||
|
* Use `create(FieldValueSourceSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const FieldValueSourceSchema: GenMessage<FieldValueSource>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ValueSource
|
||||||
|
*/
|
||||||
|
export type ValueSource = Message<"camino.ValueSource"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.FieldValueSource field = 1;
|
||||||
|
*/
|
||||||
|
field?: FieldValueSource | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ValueSource.
|
||||||
|
* Use `create(ValueSourceSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ValueSourceSchema: GenMessage<ValueSource>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.Value
|
||||||
|
*/
|
||||||
|
export type Value = Message<"camino.Value"> & {
|
||||||
|
/**
|
||||||
|
* @generated from oneof camino.Value.kind
|
||||||
|
*/
|
||||||
|
kind: {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.NullValue null_value = 1;
|
||||||
|
*/
|
||||||
|
value: NullValue;
|
||||||
|
case: "nullValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: bool bool_value = 2;
|
||||||
|
*/
|
||||||
|
value: boolean;
|
||||||
|
case: "boolValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: double number_value = 3;
|
||||||
|
*/
|
||||||
|
value: number;
|
||||||
|
case: "numberValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: string string_value = 4;
|
||||||
|
*/
|
||||||
|
value: string;
|
||||||
|
case: "stringValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: string integer_value = 5;
|
||||||
|
*/
|
||||||
|
value: string;
|
||||||
|
case: "integerValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: bytes bytes_value = 6;
|
||||||
|
*/
|
||||||
|
value: Uint8Array;
|
||||||
|
case: "bytesValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.ObjectValue object_value = 7;
|
||||||
|
*/
|
||||||
|
value: ObjectValue;
|
||||||
|
case: "objectValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.ListValue list_value = 8;
|
||||||
|
*/
|
||||||
|
value: ListValue;
|
||||||
|
case: "listValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.RefValue ref_value = 9;
|
||||||
|
*/
|
||||||
|
value: RefValue;
|
||||||
|
case: "refValue";
|
||||||
|
} | {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.CrdtValue crdt_value = 10;
|
||||||
|
*/
|
||||||
|
value: CrdtValue;
|
||||||
|
case: "crdtValue";
|
||||||
|
} | {
|
||||||
|
case: undefined;
|
||||||
|
value?: undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.ValueSource source = 11;
|
||||||
|
*/
|
||||||
|
source?: ValueSource | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.Value.
|
||||||
|
* Use `create(ValueSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ValueSchema: GenMessage<Value>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CreateObjectRequest
|
||||||
|
*/
|
||||||
|
export type CreateObjectRequest = Message<"camino.CreateObjectRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string class_id = 1;
|
||||||
|
*/
|
||||||
|
classId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, camino.Value> fields = 2;
|
||||||
|
*/
|
||||||
|
fields: {
|
||||||
|
[key: string]: Value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CreateObjectRequest.
|
||||||
|
* Use `create(CreateObjectRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CreateObjectRequestSchema: GenMessage<CreateObjectRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CreateObjectResponse
|
||||||
|
*/
|
||||||
|
export type CreateObjectResponse = Message<"camino.CreateObjectResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.CaminoObject object = 1;
|
||||||
|
*/
|
||||||
|
object?: CaminoObject | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CreateObjectResponse.
|
||||||
|
* Use `create(CreateObjectResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CreateObjectResponseSchema: GenMessage<CreateObjectResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.GetObjectRequest
|
||||||
|
*/
|
||||||
|
export type GetObjectRequest = Message<"camino.GetObjectRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.GetObjectRequest.
|
||||||
|
* Use `create(GetObjectRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const GetObjectRequestSchema: GenMessage<GetObjectRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.GetObjectResponse
|
||||||
|
*/
|
||||||
|
export type GetObjectResponse = Message<"camino.GetObjectResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.CaminoObject object = 1;
|
||||||
|
*/
|
||||||
|
object?: CaminoObject | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.GetObjectResponse.
|
||||||
|
* Use `create(GetObjectResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const GetObjectResponseSchema: GenMessage<GetObjectResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListObjectsRequest
|
||||||
|
*/
|
||||||
|
export type ListObjectsRequest = Message<"camino.ListObjectsRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string class_id = 1;
|
||||||
|
*/
|
||||||
|
classId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListObjectsRequest.
|
||||||
|
* Use `create(ListObjectsRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListObjectsRequestSchema: GenMessage<ListObjectsRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListObjectsResponse
|
||||||
|
*/
|
||||||
|
export type ListObjectsResponse = Message<"camino.ListObjectsResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.CaminoObject objects = 1;
|
||||||
|
*/
|
||||||
|
objects: CaminoObject[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListObjectsResponse.
|
||||||
|
* Use `create(ListObjectsResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListObjectsResponseSchema: GenMessage<ListObjectsResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.SetFieldRequest
|
||||||
|
*/
|
||||||
|
export type SetFieldRequest = Message<"camino.SetFieldRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string field_name = 2;
|
||||||
|
*/
|
||||||
|
fieldName: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.Value value = 3;
|
||||||
|
*/
|
||||||
|
value?: Value | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string client_mutation_id = 4;
|
||||||
|
*/
|
||||||
|
clientMutationId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.SetFieldRequest.
|
||||||
|
* Use `create(SetFieldRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const SetFieldRequestSchema: GenMessage<SetFieldRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.SetFieldResponse
|
||||||
|
*/
|
||||||
|
export type SetFieldResponse = Message<"camino.SetFieldResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.CaminoObject object = 1;
|
||||||
|
*/
|
||||||
|
object?: CaminoObject | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.SetFieldResponse.
|
||||||
|
* Use `create(SetFieldResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const SetFieldResponseSchema: GenMessage<SetFieldResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.AddEdgeRequest
|
||||||
|
*/
|
||||||
|
export type AddEdgeRequest = Message<"camino.AddEdgeRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string from_object_id = 1;
|
||||||
|
*/
|
||||||
|
fromObjectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string source_field = 2;
|
||||||
|
*/
|
||||||
|
sourceField: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string to_object_id = 3;
|
||||||
|
*/
|
||||||
|
toObjectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, camino.Value> fields = 4;
|
||||||
|
*/
|
||||||
|
fields: {
|
||||||
|
[key: string]: Value;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from field: optional int32 source_ordinal = 5;
|
||||||
|
*/
|
||||||
|
sourceOrdinal?: number | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: optional int32 target_ordinal = 6;
|
||||||
|
*/
|
||||||
|
targetOrdinal?: number | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.AddEdgeRequest.
|
||||||
|
* Use `create(AddEdgeRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const AddEdgeRequestSchema: GenMessage<AddEdgeRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.AddEdgeResponse
|
||||||
|
*/
|
||||||
|
export type AddEdgeResponse = Message<"camino.AddEdgeResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.CaminoEdge edge = 1;
|
||||||
|
*/
|
||||||
|
edge?: CaminoEdge | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.AddEdgeResponse.
|
||||||
|
* Use `create(AddEdgeResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const AddEdgeResponseSchema: GenMessage<AddEdgeResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListEdgesRequest
|
||||||
|
*/
|
||||||
|
export type ListEdgesRequest = Message<"camino.ListEdgesRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListEdgesRequest.
|
||||||
|
* Use `create(ListEdgesRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListEdgesRequestSchema: GenMessage<ListEdgesRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListEdgesResponse
|
||||||
|
*/
|
||||||
|
export type ListEdgesResponse = Message<"camino.ListEdgesResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.CaminoEdge edges = 1;
|
||||||
|
*/
|
||||||
|
edges: CaminoEdge[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListEdgesResponse.
|
||||||
|
* Use `create(ListEdgesResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListEdgesResponseSchema: GenMessage<ListEdgesResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ResolveEdgeRequest
|
||||||
|
*/
|
||||||
|
export type ResolveEdgeRequest = Message<"camino.ResolveEdgeRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string projection = 2;
|
||||||
|
*/
|
||||||
|
projection: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ResolveEdgeRequest.
|
||||||
|
* Use `create(ResolveEdgeRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ResolveEdgeRequestSchema: GenMessage<ResolveEdgeRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ResolveEdgeResponse
|
||||||
|
*/
|
||||||
|
export type ResolveEdgeResponse = Message<"camino.ResolveEdgeResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.CaminoEdge edges = 1;
|
||||||
|
*/
|
||||||
|
edges: CaminoEdge[];
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.CaminoObject materialized_object = 2;
|
||||||
|
*/
|
||||||
|
materializedObject?: CaminoObject | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool created = 3;
|
||||||
|
*/
|
||||||
|
created: boolean;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ResolveEdgeResponse.
|
||||||
|
* Use `create(ResolveEdgeResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ResolveEdgeResponseSchema: GenMessage<ResolveEdgeResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.RemoveEdgeRequest
|
||||||
|
*/
|
||||||
|
export type RemoveEdgeRequest = Message<"camino.RemoveEdgeRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string edge_id = 1;
|
||||||
|
*/
|
||||||
|
edgeId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.RemoveEdgeRequest.
|
||||||
|
* Use `create(RemoveEdgeRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const RemoveEdgeRequestSchema: GenMessage<RemoveEdgeRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.RemoveEdgeResponse
|
||||||
|
*/
|
||||||
|
export type RemoveEdgeResponse = Message<"camino.RemoveEdgeResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string edge_id = 1;
|
||||||
|
*/
|
||||||
|
edgeId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.RemoveEdgeResponse.
|
||||||
|
* Use `create(RemoveEdgeResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const RemoveEdgeResponseSchema: GenMessage<RemoveEdgeResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListOpsRequest
|
||||||
|
*/
|
||||||
|
export type ListOpsRequest = Message<"camino.ListOpsRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListOpsRequest.
|
||||||
|
* Use `create(ListOpsRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListOpsRequestSchema: GenMessage<ListOpsRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ListOpsResponse
|
||||||
|
*/
|
||||||
|
export type ListOpsResponse = Message<"camino.ListOpsResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.CaminoOp ops = 1;
|
||||||
|
*/
|
||||||
|
ops: CaminoOp[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ListOpsResponse.
|
||||||
|
* Use `create(ListOpsResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ListOpsResponseSchema: GenMessage<ListOpsResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.WatchObjectRequest
|
||||||
|
*/
|
||||||
|
export type WatchObjectRequest = Message<"camino.WatchObjectRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string after_op_id = 2;
|
||||||
|
*/
|
||||||
|
afterOpId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool include_snapshot = 3;
|
||||||
|
*/
|
||||||
|
includeSnapshot: boolean;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.WatchObjectRequest.
|
||||||
|
* Use `create(WatchObjectRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const WatchObjectRequestSchema: GenMessage<WatchObjectRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.WatchObjectEvent
|
||||||
|
*/
|
||||||
|
export type WatchObjectEvent = Message<"camino.WatchObjectEvent"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.CaminoObject snapshot = 2;
|
||||||
|
*/
|
||||||
|
snapshot?: CaminoObject | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.CaminoOp op = 3;
|
||||||
|
*/
|
||||||
|
op?: CaminoOp | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.WatchObjectEvent.
|
||||||
|
* Use `create(WatchObjectEventSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const WatchObjectEventSchema: GenMessage<WatchObjectEvent>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CaminoObject
|
||||||
|
*/
|
||||||
|
export type CaminoObject = Message<"camino.CaminoObject"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string id = 1;
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string class_id = 2;
|
||||||
|
*/
|
||||||
|
classId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: uint32 schema_version = 3;
|
||||||
|
*/
|
||||||
|
schemaVersion: number;
|
||||||
|
/**
|
||||||
|
* @generated from field: string created_at = 4;
|
||||||
|
*/
|
||||||
|
createdAt: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string updated_at = 5;
|
||||||
|
*/
|
||||||
|
updatedAt: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, camino.Value> fields = 6;
|
||||||
|
*/
|
||||||
|
fields: {
|
||||||
|
[key: string]: Value;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, uint64> field_revisions = 7;
|
||||||
|
*/
|
||||||
|
fieldRevisions: {
|
||||||
|
[key: string]: bigint;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, string> field_conflicts = 8;
|
||||||
|
*/
|
||||||
|
fieldConflicts: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CaminoObject.
|
||||||
|
* Use `create(CaminoObjectSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CaminoObjectSchema: GenMessage<CaminoObject>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CaminoEdge
|
||||||
|
*/
|
||||||
|
export type CaminoEdge = Message<"camino.CaminoEdge"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string id = 1;
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string edge_type_id = 2;
|
||||||
|
*/
|
||||||
|
edgeTypeId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string from_object_id = 3;
|
||||||
|
*/
|
||||||
|
fromObjectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string to_object_id = 4;
|
||||||
|
*/
|
||||||
|
toObjectId: string;
|
||||||
|
/**
|
||||||
|
* Legacy canonical source-side projection fields. Prefer endpoint fields.
|
||||||
|
*
|
||||||
|
* @generated from field: string source_field = 5;
|
||||||
|
*/
|
||||||
|
sourceField: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string cardinality = 6;
|
||||||
|
*/
|
||||||
|
cardinality: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string inverse = 7;
|
||||||
|
*/
|
||||||
|
inverse: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, camino.Value> fields = 8;
|
||||||
|
*/
|
||||||
|
fields: {
|
||||||
|
[key: string]: Value;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from field: optional int32 from_ordinal = 9;
|
||||||
|
*/
|
||||||
|
fromOrdinal?: number | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string created_at = 10;
|
||||||
|
*/
|
||||||
|
createdAt: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string updated_at = 11;
|
||||||
|
*/
|
||||||
|
updatedAt: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string from_projection = 12;
|
||||||
|
*/
|
||||||
|
fromProjection: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string to_projection = 13;
|
||||||
|
*/
|
||||||
|
toProjection: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string from_cardinality = 14;
|
||||||
|
*/
|
||||||
|
fromCardinality: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string to_cardinality = 15;
|
||||||
|
*/
|
||||||
|
toCardinality: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: optional int32 to_ordinal = 16;
|
||||||
|
*/
|
||||||
|
toOrdinal?: number | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string directionality = 17;
|
||||||
|
*/
|
||||||
|
directionality: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CaminoEdge.
|
||||||
|
* Use `create(CaminoEdgeSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CaminoEdgeSchema: GenMessage<CaminoEdge>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CaminoOp
|
||||||
|
*/
|
||||||
|
export type CaminoOp = Message<"camino.CaminoOp"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string id = 1;
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 2;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string op_kind = 3;
|
||||||
|
*/
|
||||||
|
opKind: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.Value payload = 4;
|
||||||
|
*/
|
||||||
|
payload?: Value | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string actor = 5;
|
||||||
|
*/
|
||||||
|
actor: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: quixos.FunctionRef function_ref = 6;
|
||||||
|
*/
|
||||||
|
functionRef?: FunctionRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string created_at = 7;
|
||||||
|
*/
|
||||||
|
createdAt: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string client_mutation_id = 8;
|
||||||
|
*/
|
||||||
|
clientMutationId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CaminoOp.
|
||||||
|
* Use `create(CaminoOpSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CaminoOpSchema: GenMessage<CaminoOp>;
|
||||||
|
/**
|
||||||
|
* @generated from service camino.CaminoService
|
||||||
|
*/
|
||||||
|
export declare const CaminoService: GenService<{
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.RegisterSchema
|
||||||
|
*/
|
||||||
|
registerSchema: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof RegisterSchemaRequestSchema;
|
||||||
|
output: typeof RegisterSchemaResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.ListClasses
|
||||||
|
*/
|
||||||
|
listClasses: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof ListClassesRequestSchema;
|
||||||
|
output: typeof ListClassesResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.CreateObject
|
||||||
|
*/
|
||||||
|
createObject: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof CreateObjectRequestSchema;
|
||||||
|
output: typeof CreateObjectResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.GetObject
|
||||||
|
*/
|
||||||
|
getObject: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof GetObjectRequestSchema;
|
||||||
|
output: typeof GetObjectResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.ListObjects
|
||||||
|
*/
|
||||||
|
listObjects: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof ListObjectsRequestSchema;
|
||||||
|
output: typeof ListObjectsResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.SetField
|
||||||
|
*/
|
||||||
|
setField: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof SetFieldRequestSchema;
|
||||||
|
output: typeof SetFieldResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.AddEdge
|
||||||
|
*/
|
||||||
|
addEdge: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof AddEdgeRequestSchema;
|
||||||
|
output: typeof AddEdgeResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.ListEdges
|
||||||
|
*/
|
||||||
|
listEdges: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof ListEdgesRequestSchema;
|
||||||
|
output: typeof ListEdgesResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.ResolveEdge
|
||||||
|
*/
|
||||||
|
resolveEdge: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof ResolveEdgeRequestSchema;
|
||||||
|
output: typeof ResolveEdgeResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.RemoveEdge
|
||||||
|
*/
|
||||||
|
removeEdge: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof RemoveEdgeRequestSchema;
|
||||||
|
output: typeof RemoveEdgeResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.ListOps
|
||||||
|
*/
|
||||||
|
listOps: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof ListOpsRequestSchema;
|
||||||
|
output: typeof ListOpsResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc camino.CaminoService.WatchObject
|
||||||
|
*/
|
||||||
|
watchObject: {
|
||||||
|
methodKind: "server_streaming";
|
||||||
|
input: typeof WatchObjectRequestSchema;
|
||||||
|
output: typeof WatchObjectEventSchema;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
//# sourceMappingURL=api_pb.d.ts.map
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+194
File diff suppressed because one or more lines are too long
Vendored
+787
@@ -0,0 +1,787 @@
|
|||||||
|
import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
||||||
|
import type { FunctionRef } from "../quixos/refs_pb.js";
|
||||||
|
import type { Message } from "@bufbuild/protobuf";
|
||||||
|
/**
|
||||||
|
* Describes the file camino/schema.proto.
|
||||||
|
*/
|
||||||
|
export declare const file_camino_schema: GenFile;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.SymbolRef
|
||||||
|
*/
|
||||||
|
export type SymbolRef = Message<"camino.SymbolRef"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string namespace = 1;
|
||||||
|
*/
|
||||||
|
namespace: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string name = 2;
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string version = 3;
|
||||||
|
*/
|
||||||
|
version: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string hash = 4;
|
||||||
|
*/
|
||||||
|
hash: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.SymbolRef.
|
||||||
|
* Use `create(SymbolRefSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const SymbolRefSchema: GenMessage<SymbolRef>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.Ref
|
||||||
|
*/
|
||||||
|
export type Ref = Message<"camino.Ref"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 1;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.Ref.
|
||||||
|
* Use `create(RefSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const RefSchema: GenMessage<Ref>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CustomField
|
||||||
|
*/
|
||||||
|
export type CustomField = Message<"camino.CustomField"> & {};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CustomField.
|
||||||
|
* Use `create(CustomFieldSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CustomFieldSchema: GenMessage<CustomField>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.WatchStartResult
|
||||||
|
*/
|
||||||
|
export type WatchStartResult = Message<"camino.WatchStartResult"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string watch_id = 1;
|
||||||
|
*/
|
||||||
|
watchId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.WatchStartResult.
|
||||||
|
* Use `create(WatchStartResultSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const WatchStartResultSchema: GenMessage<WatchStartResult>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.WatchStopRequest
|
||||||
|
*/
|
||||||
|
export type WatchStopRequest = Message<"camino.WatchStopRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string watch_id = 1;
|
||||||
|
*/
|
||||||
|
watchId: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.WatchStopRequest.
|
||||||
|
* Use `create(WatchStopRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const WatchStopRequestSchema: GenMessage<WatchStopRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.WatchStopResult
|
||||||
|
*/
|
||||||
|
export type WatchStopResult = Message<"camino.WatchStopResult"> & {};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.WatchStopResult.
|
||||||
|
* Use `create(WatchStopResultSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const WatchStopResultSchema: GenMessage<WatchStopResult>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CrdtText
|
||||||
|
*/
|
||||||
|
export type CrdtText = Message<"camino.CrdtText"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: bytes payload = 1;
|
||||||
|
*/
|
||||||
|
payload: Uint8Array;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CrdtText.
|
||||||
|
* Use `create(CrdtTextSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CrdtTextSchema: GenMessage<CrdtText>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CrdtRichText
|
||||||
|
*/
|
||||||
|
export type CrdtRichText = Message<"camino.CrdtRichText"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: bytes payload = 1;
|
||||||
|
*/
|
||||||
|
payload: Uint8Array;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CrdtRichText.
|
||||||
|
* Use `create(CrdtRichTextSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CrdtRichTextSchema: GenMessage<CrdtRichText>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.CrdtJson
|
||||||
|
*/
|
||||||
|
export type CrdtJson = Message<"camino.CrdtJson"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: bytes payload = 1;
|
||||||
|
*/
|
||||||
|
payload: Uint8Array;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.CrdtJson.
|
||||||
|
* Use `create(CrdtJsonSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const CrdtJsonSchema: GenMessage<CrdtJson>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ClassSchema
|
||||||
|
*/
|
||||||
|
export type ClassSchema = Message<"camino.ClassSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef id = 1;
|
||||||
|
*/
|
||||||
|
id?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: uint32 version = 2;
|
||||||
|
*/
|
||||||
|
version: number;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.FieldSchema fields = 3;
|
||||||
|
*/
|
||||||
|
fields: FieldSchema[];
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.EdgeSchema edges = 4;
|
||||||
|
*/
|
||||||
|
edges: EdgeSchema[];
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.MethodSchema methods = 5;
|
||||||
|
*/
|
||||||
|
methods: MethodSchema[];
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.MigrationSpec migrations = 6;
|
||||||
|
*/
|
||||||
|
migrations: MigrationSpec[];
|
||||||
|
/**
|
||||||
|
* @generated from field: string source_file = 7;
|
||||||
|
*/
|
||||||
|
sourceFile: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string proto_message = 8;
|
||||||
|
*/
|
||||||
|
protoMessage: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.OperationServiceSchema operation_services = 9;
|
||||||
|
*/
|
||||||
|
operationServices: OperationServiceSchema[];
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.InterfaceImplementationSchema implements = 10;
|
||||||
|
*/
|
||||||
|
implements: InterfaceImplementationSchema[];
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.ConstructorSpec constructor = 11;
|
||||||
|
*/
|
||||||
|
constructor$?: ConstructorSpec | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ClassSchema.
|
||||||
|
* Use `create(ClassSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ClassSchemaSchema: GenMessage<ClassSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.FieldSchema
|
||||||
|
*/
|
||||||
|
export type FieldSchema = Message<"camino.FieldSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string name = 1;
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: uint32 tag = 2;
|
||||||
|
*/
|
||||||
|
tag: number;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.TypeRef type = 3;
|
||||||
|
*/
|
||||||
|
type?: TypeRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.ConflictStrategy conflict = 4;
|
||||||
|
*/
|
||||||
|
conflict: ConflictStrategy;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool repeated = 5;
|
||||||
|
*/
|
||||||
|
repeated: boolean;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool optional = 6;
|
||||||
|
*/
|
||||||
|
optional: boolean;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.FieldStorage storage = 7;
|
||||||
|
*/
|
||||||
|
storage?: FieldStorage | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.FieldOps ops = 8;
|
||||||
|
*/
|
||||||
|
ops?: FieldOps | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.InterfaceFieldContract interface_contract = 9;
|
||||||
|
*/
|
||||||
|
interfaceContract?: InterfaceFieldContract | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool is_display_label = 10;
|
||||||
|
*/
|
||||||
|
isDisplayLabel: boolean;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.FieldSchema.
|
||||||
|
* Use `create(FieldSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const FieldSchemaSchema: GenMessage<FieldSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.FieldStorage
|
||||||
|
*/
|
||||||
|
export type FieldStorage = Message<"camino.FieldStorage"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.FieldStorageKind kind = 1;
|
||||||
|
*/
|
||||||
|
kind: FieldStorageKind;
|
||||||
|
/**
|
||||||
|
* @generated from field: quixos.FunctionRef resolver = 2;
|
||||||
|
*/
|
||||||
|
resolver?: FunctionRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool cache = 3;
|
||||||
|
*/
|
||||||
|
cache: boolean;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.FieldStorage.
|
||||||
|
* Use `create(FieldStorageSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const FieldStorageSchema: GenMessage<FieldStorage>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.TypeRef
|
||||||
|
*/
|
||||||
|
export type TypeRef = Message<"camino.TypeRef"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string proto_type = 1;
|
||||||
|
*/
|
||||||
|
protoType: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef symbol = 2;
|
||||||
|
*/
|
||||||
|
symbol?: SymbolRef | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.TypeRef.
|
||||||
|
* Use `create(TypeRefSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const TypeRefSchema: GenMessage<TypeRef>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.TypeBinding
|
||||||
|
*/
|
||||||
|
export type TypeBinding = Message<"camino.TypeBinding"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string name = 1;
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.TypeRef type = 2;
|
||||||
|
*/
|
||||||
|
type?: TypeRef | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.TypeBinding.
|
||||||
|
* Use `create(TypeBindingSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const TypeBindingSchema: GenMessage<TypeBinding>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.InterfaceFieldContract
|
||||||
|
*/
|
||||||
|
export type InterfaceFieldContract = Message<"camino.InterfaceFieldContract"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: bool required = 1;
|
||||||
|
*/
|
||||||
|
required: boolean;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool readable = 2;
|
||||||
|
*/
|
||||||
|
readable: boolean;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool writable = 3;
|
||||||
|
*/
|
||||||
|
writable: boolean;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool watchable = 4;
|
||||||
|
*/
|
||||||
|
watchable: boolean;
|
||||||
|
/**
|
||||||
|
* @generated from field: string type_param = 5;
|
||||||
|
*/
|
||||||
|
typeParam: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.TypeRef type = 6;
|
||||||
|
*/
|
||||||
|
type?: TypeRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string ref_target_type_param = 7;
|
||||||
|
*/
|
||||||
|
refTargetTypeParam: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.Cardinality edge_cardinality = 8;
|
||||||
|
*/
|
||||||
|
edgeCardinality: Cardinality;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.InterfaceFieldContract.
|
||||||
|
* Use `create(InterfaceFieldContractSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const InterfaceFieldContractSchema: GenMessage<InterfaceFieldContract>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.InterfaceImplementationSchema
|
||||||
|
*/
|
||||||
|
export type InterfaceImplementationSchema = Message<"camino.InterfaceImplementationSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef interface = 1;
|
||||||
|
*/
|
||||||
|
interface?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.TypeBinding type_bindings = 2;
|
||||||
|
*/
|
||||||
|
typeBindings: TypeBinding[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.InterfaceImplementationSchema.
|
||||||
|
* Use `create(InterfaceImplementationSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const InterfaceImplementationSchemaSchema: GenMessage<InterfaceImplementationSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.InterfaceSchema
|
||||||
|
*/
|
||||||
|
export type InterfaceSchema = Message<"camino.InterfaceSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef id = 1;
|
||||||
|
*/
|
||||||
|
id?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: uint32 version = 2;
|
||||||
|
*/
|
||||||
|
version: number;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.FieldSchema fields = 3;
|
||||||
|
*/
|
||||||
|
fields: FieldSchema[];
|
||||||
|
/**
|
||||||
|
* @generated from field: string source_file = 4;
|
||||||
|
*/
|
||||||
|
sourceFile: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string proto_message = 5;
|
||||||
|
*/
|
||||||
|
protoMessage: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated string type_parameters = 6;
|
||||||
|
*/
|
||||||
|
typeParameters: string[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.InterfaceSchema.
|
||||||
|
* Use `create(InterfaceSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const InterfaceSchemaSchema: GenMessage<InterfaceSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.FieldOps
|
||||||
|
*/
|
||||||
|
export type FieldOps = Message<"camino.FieldOps"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.FieldImplementation implementation = 1;
|
||||||
|
*/
|
||||||
|
implementation: FieldImplementation;
|
||||||
|
/**
|
||||||
|
* @generated from field: string service = 2;
|
||||||
|
*/
|
||||||
|
service: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.FieldOps.
|
||||||
|
* Use `create(FieldOpsSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const FieldOpsSchema: GenMessage<FieldOps>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.EdgeSchema
|
||||||
|
*/
|
||||||
|
export type EdgeSchema = Message<"camino.EdgeSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef id = 1;
|
||||||
|
*/
|
||||||
|
id?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* Legacy/source-side projection fields. Prefer from_endpoint/to_endpoint.
|
||||||
|
*
|
||||||
|
* @generated from field: string source_field = 2;
|
||||||
|
*/
|
||||||
|
sourceField: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef from_class = 3;
|
||||||
|
*/
|
||||||
|
fromClass?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef to_class = 4;
|
||||||
|
*/
|
||||||
|
toClass?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.Cardinality cardinality = 5;
|
||||||
|
*/
|
||||||
|
cardinality: Cardinality;
|
||||||
|
/**
|
||||||
|
* @generated from field: string inverse = 6;
|
||||||
|
*/
|
||||||
|
inverse: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.FieldSchema fields = 7;
|
||||||
|
*/
|
||||||
|
fields: FieldSchema[];
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.EdgeEndpointSchema from_endpoint = 8;
|
||||||
|
*/
|
||||||
|
fromEndpoint?: EdgeEndpointSchema | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.EdgeEndpointSchema to_endpoint = 9;
|
||||||
|
*/
|
||||||
|
toEndpoint?: EdgeEndpointSchema | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef declaring_class = 10;
|
||||||
|
*/
|
||||||
|
declaringClass?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.SymbolRef tags = 11;
|
||||||
|
*/
|
||||||
|
tags: SymbolRef[];
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.SymbolRef implements = 12;
|
||||||
|
*/
|
||||||
|
implements: SymbolRef[];
|
||||||
|
/**
|
||||||
|
* @generated from field: bool undirected = 13;
|
||||||
|
*/
|
||||||
|
undirected: boolean;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.EdgeSchema.
|
||||||
|
* Use `create(EdgeSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const EdgeSchemaSchema: GenMessage<EdgeSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.EdgeEndpointSchema
|
||||||
|
*/
|
||||||
|
export type EdgeEndpointSchema = Message<"camino.EdgeEndpointSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef class = 1;
|
||||||
|
*/
|
||||||
|
class?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef interface = 2;
|
||||||
|
*/
|
||||||
|
interface?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string projection = 3;
|
||||||
|
*/
|
||||||
|
projection: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.Cardinality cardinality = 4;
|
||||||
|
*/
|
||||||
|
cardinality: Cardinality;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool indexed = 5;
|
||||||
|
*/
|
||||||
|
indexed: boolean;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.EdgeMaterialization materialize = 6;
|
||||||
|
*/
|
||||||
|
materialize?: EdgeMaterialization | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.EdgeEndpointSchema.
|
||||||
|
* Use `create(EdgeEndpointSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const EdgeEndpointSchemaSchema: GenMessage<EdgeEndpointSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.EdgeMaterialization
|
||||||
|
*/
|
||||||
|
export type EdgeMaterialization = Message<"camino.EdgeMaterialization"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef class = 1;
|
||||||
|
*/
|
||||||
|
class?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string connect_projection = 2;
|
||||||
|
*/
|
||||||
|
connectProjection: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.EdgeMaterialization.
|
||||||
|
* Use `create(EdgeMaterializationSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const EdgeMaterializationSchema: GenMessage<EdgeMaterialization>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.MethodSchema
|
||||||
|
*/
|
||||||
|
export type MethodSchema = Message<"camino.MethodSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string name = 1;
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: quixos.FunctionRef function = 2;
|
||||||
|
*/
|
||||||
|
function?: FunctionRef | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.MethodSchema.
|
||||||
|
* Use `create(MethodSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const MethodSchemaSchema: GenMessage<MethodSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.MigrationSpec
|
||||||
|
*/
|
||||||
|
export type MigrationSpec = Message<"camino.MigrationSpec"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: uint32 from_version = 1;
|
||||||
|
*/
|
||||||
|
fromVersion: number;
|
||||||
|
/**
|
||||||
|
* @generated from field: uint32 to_version = 2;
|
||||||
|
*/
|
||||||
|
toVersion: number;
|
||||||
|
/**
|
||||||
|
* @generated from field: quixos.FunctionRef function = 3;
|
||||||
|
*/
|
||||||
|
function?: FunctionRef | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.MigrationSpec.
|
||||||
|
* Use `create(MigrationSpecSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const MigrationSpecSchema: GenMessage<MigrationSpec>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.ConstructorSpec
|
||||||
|
*/
|
||||||
|
export type ConstructorSpec = Message<"camino.ConstructorSpec"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: quixos.FunctionRef function = 1;
|
||||||
|
*/
|
||||||
|
function?: FunctionRef | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.ConstructorSpec.
|
||||||
|
* Use `create(ConstructorSpecSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const ConstructorSpecSchema: GenMessage<ConstructorSpec>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.OperationSchema
|
||||||
|
*/
|
||||||
|
export type OperationSchema = Message<"camino.OperationSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string name = 1;
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.TypeRef input_type = 2;
|
||||||
|
*/
|
||||||
|
inputType?: TypeRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.TypeRef output_type = 3;
|
||||||
|
*/
|
||||||
|
outputType?: TypeRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: quixos.FunctionRef function = 4;
|
||||||
|
*/
|
||||||
|
function?: FunctionRef | undefined;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.OperationSchema.
|
||||||
|
* Use `create(OperationSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const OperationSchemaSchema: GenMessage<OperationSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.OperationServiceSchema
|
||||||
|
*/
|
||||||
|
export type OperationServiceSchema = Message<"camino.OperationServiceSchema"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string name = 1;
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string full_name = 2;
|
||||||
|
*/
|
||||||
|
fullName: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated camino.OperationSchema operations = 3;
|
||||||
|
*/
|
||||||
|
operations: OperationSchema[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.OperationServiceSchema.
|
||||||
|
* Use `create(OperationServiceSchemaSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const OperationServiceSchemaSchema: GenMessage<OperationServiceSchema>;
|
||||||
|
/**
|
||||||
|
* @generated from message camino.DocRefDeclaration
|
||||||
|
*/
|
||||||
|
export type DocRefDeclaration = Message<"camino.DocRefDeclaration"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string local_name = 1;
|
||||||
|
*/
|
||||||
|
localName: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef type = 2;
|
||||||
|
*/
|
||||||
|
type?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.SymbolRef target_class = 3;
|
||||||
|
*/
|
||||||
|
targetClass?: SymbolRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.DocRefStrength strength = 4;
|
||||||
|
*/
|
||||||
|
strength: DocRefStrength;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message camino.DocRefDeclaration.
|
||||||
|
* Use `create(DocRefDeclarationSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const DocRefDeclarationSchema: GenMessage<DocRefDeclaration>;
|
||||||
|
/**
|
||||||
|
* @generated from enum camino.ConflictStrategy
|
||||||
|
*/
|
||||||
|
export declare enum ConflictStrategy {
|
||||||
|
/**
|
||||||
|
* @generated from enum value: CONFLICT_STRATEGY_UNSPECIFIED = 0;
|
||||||
|
*/
|
||||||
|
CONFLICT_STRATEGY_UNSPECIFIED = 0,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: REPLACE = 1;
|
||||||
|
*/
|
||||||
|
REPLACE = 1,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: PRESERVE_CONFLICTS = 2;
|
||||||
|
*/
|
||||||
|
PRESERVE_CONFLICTS = 2,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: CRDT = 3;
|
||||||
|
*/
|
||||||
|
CRDT = 3
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Describes the enum camino.ConflictStrategy.
|
||||||
|
*/
|
||||||
|
export declare const ConflictStrategySchema: GenEnum<ConflictStrategy>;
|
||||||
|
/**
|
||||||
|
* @generated from enum camino.Cardinality
|
||||||
|
*/
|
||||||
|
export declare enum Cardinality {
|
||||||
|
/**
|
||||||
|
* @generated from enum value: CARDINALITY_UNSPECIFIED = 0;
|
||||||
|
*/
|
||||||
|
CARDINALITY_UNSPECIFIED = 0,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: OPTIONAL_ONE = 1;
|
||||||
|
*/
|
||||||
|
OPTIONAL_ONE = 1,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: EXACTLY_ONE = 2;
|
||||||
|
*/
|
||||||
|
EXACTLY_ONE = 2,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: MANY = 3;
|
||||||
|
*/
|
||||||
|
MANY = 3,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: MANY_UNIQUE = 4;
|
||||||
|
*/
|
||||||
|
MANY_UNIQUE = 4,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: MANY_ORDERED = 5;
|
||||||
|
*/
|
||||||
|
MANY_ORDERED = 5,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: MANY_UNIQUE_ORDERED = 6;
|
||||||
|
*/
|
||||||
|
MANY_UNIQUE_ORDERED = 6
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Describes the enum camino.Cardinality.
|
||||||
|
*/
|
||||||
|
export declare const CardinalitySchema: GenEnum<Cardinality>;
|
||||||
|
/**
|
||||||
|
* @generated from enum camino.DocRefStrength
|
||||||
|
*/
|
||||||
|
export declare enum DocRefStrength {
|
||||||
|
/**
|
||||||
|
* @generated from enum value: DOC_REF_STRENGTH_UNSPECIFIED = 0;
|
||||||
|
*/
|
||||||
|
DOC_REF_STRENGTH_UNSPECIFIED = 0,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: SOFT_REF = 1;
|
||||||
|
*/
|
||||||
|
SOFT_REF = 1,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: PROJECTED_EDGE = 2;
|
||||||
|
*/
|
||||||
|
PROJECTED_EDGE = 2
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Describes the enum camino.DocRefStrength.
|
||||||
|
*/
|
||||||
|
export declare const DocRefStrengthSchema: GenEnum<DocRefStrength>;
|
||||||
|
/**
|
||||||
|
* @generated from enum camino.FieldStorageKind
|
||||||
|
*/
|
||||||
|
export declare enum FieldStorageKind {
|
||||||
|
/**
|
||||||
|
* @generated from enum value: FIELD_STORAGE_KIND_UNSPECIFIED = 0;
|
||||||
|
*/
|
||||||
|
FIELD_STORAGE_KIND_UNSPECIFIED = 0,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: STORED = 1;
|
||||||
|
*/
|
||||||
|
STORED = 1,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: DERIVED = 2;
|
||||||
|
*/
|
||||||
|
DERIVED = 2,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: LAZY = 3;
|
||||||
|
*/
|
||||||
|
LAZY = 3,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: EXTERNAL = 4;
|
||||||
|
*/
|
||||||
|
EXTERNAL = 4,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: STATIC_FINAL = 5;
|
||||||
|
*/
|
||||||
|
STATIC_FINAL = 5
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Describes the enum camino.FieldStorageKind.
|
||||||
|
*/
|
||||||
|
export declare const FieldStorageKindSchema: GenEnum<FieldStorageKind>;
|
||||||
|
/**
|
||||||
|
* @generated from enum camino.FieldImplementation
|
||||||
|
*/
|
||||||
|
export declare enum FieldImplementation {
|
||||||
|
/**
|
||||||
|
* @generated from enum value: FIELD_IMPLEMENTATION_UNSPECIFIED = 0;
|
||||||
|
*/
|
||||||
|
FIELD_IMPLEMENTATION_UNSPECIFIED = 0,
|
||||||
|
/**
|
||||||
|
* @generated from enum value: SERVICE = 1;
|
||||||
|
*/
|
||||||
|
SERVICE = 1
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Describes the enum camino.FieldImplementation.
|
||||||
|
*/
|
||||||
|
export declare const FieldImplementationSchema: GenEnum<FieldImplementation>;
|
||||||
|
//# sourceMappingURL=schema_pb.d.ts.map
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+282
File diff suppressed because one or more lines are too long
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=codegen-ts-runtime.d.ts.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"codegen-ts-runtime.d.ts","sourceRoot":"","sources":["../src/codegen-ts-runtime.ts"],"names":[],"mappings":""}
|
||||||
+524
@@ -0,0 +1,524 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import protobuf from "protobufjs";
|
||||||
|
import { compileCaminoSchema, loadCaminoSchemaRoot, } from "./schema-compiler.js";
|
||||||
|
const usage = () => {
|
||||||
|
console.error("Usage: camino-codegen-ts-runtime [--react-only] <class-schema.camino.proto> <out-dir> [implementation-import]");
|
||||||
|
process.exit(1);
|
||||||
|
};
|
||||||
|
const required = (value) => value ?? usage();
|
||||||
|
const stringLiteral = (value) => JSON.stringify(value);
|
||||||
|
const upperFirst = (value) => value.length === 0 ? value : `${value[0]?.toUpperCase()}${value.slice(1)}`;
|
||||||
|
const lowerFirst = (value) => value.length === 0 ? value : `${value[0]?.toLowerCase()}${value.slice(1)}`;
|
||||||
|
const camel = (value) => value
|
||||||
|
.split(/[^a-zA-Z0-9]+/)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((part, index) => index === 0 ? lowerFirst(part) : upperFirst(lowerFirst(part)))
|
||||||
|
.join("");
|
||||||
|
const isIdentifier = (value) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
|
||||||
|
const classId = (schema) => `${schema.id.namespace}:${schema.id.name}:${schema.id.version}:${schema.id.hash ?? ""}`;
|
||||||
|
const symbolId = (value) => `${value.namespace}:${value.name}:${value.version}:${value.hash ?? ""}`;
|
||||||
|
const normalizedProtoType = (value) => value.replace(/^\./, "");
|
||||||
|
const declarationName = (value) => value.name;
|
||||||
|
const tsTypeForTypeRef = (type, root) => {
|
||||||
|
if (type.symbol) {
|
||||||
|
return `ObjectRef<${stringLiteral(symbolId(type.symbol))}>`;
|
||||||
|
}
|
||||||
|
if (type.enumValues && type.enumValues.length > 0) {
|
||||||
|
return type.enumValues.map(stringLiteral).join(" | ");
|
||||||
|
}
|
||||||
|
const protoType = normalizedProtoType(type.protoType);
|
||||||
|
switch (protoType) {
|
||||||
|
case "string":
|
||||||
|
case "google.protobuf.StringValue":
|
||||||
|
return "string";
|
||||||
|
case "bool":
|
||||||
|
case "google.protobuf.BoolValue":
|
||||||
|
return "boolean";
|
||||||
|
case "double":
|
||||||
|
case "float":
|
||||||
|
case "google.protobuf.DoubleValue":
|
||||||
|
case "google.protobuf.FloatValue":
|
||||||
|
case "int32":
|
||||||
|
case "sint32":
|
||||||
|
case "sfixed32":
|
||||||
|
case "uint32":
|
||||||
|
case "fixed32":
|
||||||
|
case "int64":
|
||||||
|
case "sint64":
|
||||||
|
case "sfixed64":
|
||||||
|
case "uint64":
|
||||||
|
case "fixed64":
|
||||||
|
case "google.protobuf.Int32Value":
|
||||||
|
case "google.protobuf.UInt32Value":
|
||||||
|
case "google.protobuf.Int64Value":
|
||||||
|
case "google.protobuf.UInt64Value":
|
||||||
|
return "number";
|
||||||
|
case "bytes":
|
||||||
|
case "google.protobuf.BytesValue":
|
||||||
|
return "Uint8Array";
|
||||||
|
default:
|
||||||
|
if (protoType === "google.protobuf.Empty") {
|
||||||
|
return "Record<string, never>";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const reflected = root?.lookup(protoType);
|
||||||
|
if (reflected instanceof protobuf.Type || reflected instanceof protobuf.Enum) {
|
||||||
|
return declarationName(reflected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// Unknown imported message types remain opaque at this codegen boundary.
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const tsTypeForField = (field, root) => tsTypeForTypeRef(field.type, root);
|
||||||
|
const tsValueTypeForField = (field, root) => field.repeated ? `${tsTypeForField(field, root)}[]` : tsTypeForField(field, root);
|
||||||
|
const tsFieldApiType = (field, root) => field.conflict === "crdt"
|
||||||
|
? `CrdtField<${tsValueTypeForField(field, root)}>`
|
||||||
|
: `Field<${tsValueTypeForField(field, root)}>`;
|
||||||
|
const functionExportName = (fn, fallback) => {
|
||||||
|
if (isIdentifier(fn.symbol)) {
|
||||||
|
return fn.symbol;
|
||||||
|
}
|
||||||
|
const fallbackName = camel(fallback);
|
||||||
|
if (isIdentifier(fallbackName)) {
|
||||||
|
return fallbackName;
|
||||||
|
}
|
||||||
|
throw new Error(`Cannot generate TS export name for function ${fn.symbol}`);
|
||||||
|
};
|
||||||
|
const generatedWarning = `// @generated by camino-codegen-ts-runtime. Do not edit.\n`;
|
||||||
|
const scalarTsType = (field, root) => {
|
||||||
|
const primitive = tsTypeForTypeRef({ protoType: field.type }, root);
|
||||||
|
if (primitive !== "unknown") {
|
||||||
|
return primitive;
|
||||||
|
}
|
||||||
|
const resolved = field.resolvedType;
|
||||||
|
if (resolved instanceof protobuf.Type || resolved instanceof protobuf.Enum) {
|
||||||
|
return declarationName(resolved);
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
};
|
||||||
|
const collectProtoDeclarations = (root, types) => {
|
||||||
|
const declarations = new Map();
|
||||||
|
const visit = (value) => {
|
||||||
|
const key = value.fullName;
|
||||||
|
if (declarations.has(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
declarations.set(key, value);
|
||||||
|
if (value instanceof protobuf.Type) {
|
||||||
|
for (const field of value.fieldsArray) {
|
||||||
|
if (field.resolvedType instanceof protobuf.Type ||
|
||||||
|
field.resolvedType instanceof protobuf.Enum) {
|
||||||
|
visit(field.resolvedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const type of types) {
|
||||||
|
if (type.symbol || !type.protoType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const reflected = root.lookup(normalizedProtoType(type.protoType));
|
||||||
|
if (reflected instanceof protobuf.Type || reflected instanceof protobuf.Enum) {
|
||||||
|
visit(reflected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// Opaque imported types are represented as unknown by the generated API.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...declarations.values()]
|
||||||
|
.map((value) => {
|
||||||
|
if (value instanceof protobuf.Enum) {
|
||||||
|
return `export type ${declarationName(value)} = ${Object.keys(value.values)
|
||||||
|
.map(stringLiteral)
|
||||||
|
.join(" | ")};`;
|
||||||
|
}
|
||||||
|
const fields = value.fieldsArray.map((field) => {
|
||||||
|
const fieldType = scalarTsType(field, root);
|
||||||
|
const valueType = field.map
|
||||||
|
? `Record<string, ${fieldType}>`
|
||||||
|
: field.repeated
|
||||||
|
? `${fieldType}[]`
|
||||||
|
: fieldType;
|
||||||
|
return ` ${JSON.stringify(field.name)}${field.optional ? "?" : ""}: ${valueType};`;
|
||||||
|
});
|
||||||
|
return `export type ${declarationName(value)} = {\n${fields.join("\n")}\n};`;
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
|
};
|
||||||
|
const fieldTypeId = (field) => field.type.symbol ? symbolId(field.type.symbol) : field.type.protoType;
|
||||||
|
const crdtStorageTypeId = (field) => field.type.protoType ||
|
||||||
|
(field.type.symbol
|
||||||
|
? `${field.type.symbol.namespace}.${field.type.symbol.name}`
|
||||||
|
: fieldTypeId(field));
|
||||||
|
const fieldStorageId = (field) => field.ops && field.storage.kind === "stored" ? "derived" : field.storage.kind;
|
||||||
|
const interfaceTypeName = (implementation) => `${implementation.interface.name}Ref`;
|
||||||
|
const renderInterfaceRefTypes = (schema) => schema.implements
|
||||||
|
.map((implementation) => {
|
||||||
|
const typeParams = implementation.typeBindings.map((binding) => binding.name);
|
||||||
|
const generic = typeParams.length > 0 ? `<${typeParams.join(", ")}>` : "";
|
||||||
|
const bindings = typeParams.length > 0
|
||||||
|
? typeParams
|
||||||
|
.map((param) => ` readonly ${param}: ${param};`)
|
||||||
|
.join("\n")
|
||||||
|
: " readonly value: unknown;";
|
||||||
|
return `export type ${interfaceTypeName(implementation)}${generic} = ObjectRef<string> & {
|
||||||
|
readonly $caminoInterfaces: {
|
||||||
|
readonly ${stringLiteral(symbolId(implementation.interface))}: {
|
||||||
|
${bindings}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};`;
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
|
const interfaceMixins = (schema, root) => schema.implements.map((implementation) => {
|
||||||
|
const typeArgs = implementation.typeBindings.map((binding) => tsTypeForTypeRef(binding.type, root));
|
||||||
|
return `${interfaceTypeName(implementation)}${typeArgs.length > 0 ? `<${typeArgs.join(", ")}>` : ""}`;
|
||||||
|
});
|
||||||
|
const operationFunctionSpecs = (schema) => schema.fields.flatMap((field) => {
|
||||||
|
const serviceName = field.ops?.service.replace(/^\./, "");
|
||||||
|
if (!serviceName) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const service = schema.operationServices.find((candidate) => candidate.fullName === serviceName);
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`${schema.id.name}.${field.name} points to missing operation service ${field.ops?.service}`);
|
||||||
|
}
|
||||||
|
return service.operations.map((operation) => ({
|
||||||
|
exportName: functionExportName(operation.function, `${field.name}_${operation.name}`),
|
||||||
|
symbol: operation.function.symbol,
|
||||||
|
...(operation.function.operation
|
||||||
|
? { operation: operation.function.operation }
|
||||||
|
: {}),
|
||||||
|
packageNamespace: operation.function.packageNamespace,
|
||||||
|
packageName: operation.function.packageName,
|
||||||
|
field: {
|
||||||
|
name: field.name,
|
||||||
|
type: fieldTypeId(field),
|
||||||
|
storage: fieldStorageId(field),
|
||||||
|
conflict: field.conflict,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const renderRuntime = (schema, root) => {
|
||||||
|
const className = schema.id.name;
|
||||||
|
const objectTypeName = `${className}Object`;
|
||||||
|
const refTypeName = `${className}Ref`;
|
||||||
|
const contextTypeName = `${className}Context`;
|
||||||
|
const implementationTypeName = `${className}Implementation`;
|
||||||
|
const serveName = `serve${className}Runtime`;
|
||||||
|
const createObjectName = `create${className}Object`;
|
||||||
|
const interfaceRefTypes = renderInterfaceRefTypes(schema);
|
||||||
|
const refType = [`ObjectRef<${stringLiteral(classId(schema))}>`, ...interfaceMixins(schema, root)]
|
||||||
|
.join(" & ");
|
||||||
|
const edgeFields = new Set(schema.edges.map((edge) => edge.sourceField));
|
||||||
|
const writableFields = schema.fields.filter((field) => field.storage.kind === "stored" &&
|
||||||
|
!field.ops &&
|
||||||
|
!edgeFields.has(field.name));
|
||||||
|
const functionSpecs = [
|
||||||
|
...(schema.constructorSpec
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
exportName: functionExportName(schema.constructorSpec.function, "constructor"),
|
||||||
|
symbol: schema.constructorSpec.function.symbol,
|
||||||
|
...(schema.constructorSpec.function.operation
|
||||||
|
? { operation: schema.constructorSpec.function.operation }
|
||||||
|
: {}),
|
||||||
|
packageNamespace: schema.constructorSpec.function.packageNamespace,
|
||||||
|
packageName: schema.constructorSpec.function.packageName,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...schema.methods.map((method) => ({
|
||||||
|
exportName: functionExportName(method.function, method.name),
|
||||||
|
symbol: method.function.symbol,
|
||||||
|
...(method.function.operation ? { operation: method.function.operation } : {}),
|
||||||
|
packageNamespace: method.function.packageNamespace,
|
||||||
|
packageName: method.function.packageName,
|
||||||
|
})),
|
||||||
|
...schema.fields.flatMap((field) => field.storage.resolver
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
exportName: functionExportName(field.storage.resolver, field.name),
|
||||||
|
symbol: field.storage.resolver.symbol,
|
||||||
|
...(field.storage.resolver.operation
|
||||||
|
? { operation: field.storage.resolver.operation }
|
||||||
|
: {}),
|
||||||
|
packageNamespace: field.storage.resolver.packageNamespace,
|
||||||
|
packageName: field.storage.resolver.packageName,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...operationFunctionSpecs(schema),
|
||||||
|
];
|
||||||
|
const firstFunction = functionSpecs[0];
|
||||||
|
if (!firstFunction) {
|
||||||
|
throw new Error(`${schema.id.name} has no runtime functions to generate`);
|
||||||
|
}
|
||||||
|
const mixedPackage = functionSpecs.find((spec) => spec.packageNamespace !== firstFunction.packageNamespace ||
|
||||||
|
spec.packageName !== firstFunction.packageName);
|
||||||
|
if (mixedPackage) {
|
||||||
|
throw new Error(`${schema.id.name} points to multiple packages; v0 TS runtime generation expects one package`);
|
||||||
|
}
|
||||||
|
return `${generatedWarning}import {
|
||||||
|
type CaminoClient,
|
||||||
|
createCrdtField,
|
||||||
|
createField,
|
||||||
|
type CrdtField,
|
||||||
|
type Field,
|
||||||
|
type FieldOperation,
|
||||||
|
type InvokeRequest,
|
||||||
|
objectRef,
|
||||||
|
type ObjectRef,
|
||||||
|
type RuntimeHandler,
|
||||||
|
serveQuixosPackageRuntime,
|
||||||
|
} from "@quixos/camino-package-runtime";
|
||||||
|
|
||||||
|
${collectProtoDeclarations(root, schema.implements.flatMap((entry) => entry.typeBindings.map((binding) => binding.type)))}
|
||||||
|
|
||||||
|
${interfaceRefTypes ? `${interfaceRefTypes}\n\n` : ""}export type ${refTypeName} = ${refType};
|
||||||
|
|
||||||
|
export type ${objectTypeName} = {
|
||||||
|
id: ${refTypeName};
|
||||||
|
${writableFields
|
||||||
|
.map((field) => ` ${camel(field.name)}: ${tsFieldApiType(field, root)};`)
|
||||||
|
.join("\n")}
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ${contextTypeName} = {
|
||||||
|
object: ${objectTypeName};
|
||||||
|
camino: CaminoClient;
|
||||||
|
request: InvokeRequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ${implementationTypeName} = {
|
||||||
|
${functionSpecs
|
||||||
|
.map((spec) => spec.operation
|
||||||
|
? ` ${spec.exportName}: FieldOperation<${contextTypeName}>;`
|
||||||
|
: ` ${spec.exportName}: RuntimeHandler<${contextTypeName}>;`)
|
||||||
|
.filter((line, index, lines) => lines.indexOf(line) === index)
|
||||||
|
.join("\n")}
|
||||||
|
};
|
||||||
|
|
||||||
|
const packageNamespace = ${stringLiteral(firstFunction.packageNamespace)};
|
||||||
|
const packageName = ${stringLiteral(firstFunction.packageName)};
|
||||||
|
const runtimeProtocolVersion = ${stringLiteral("camino-orch-v0")};
|
||||||
|
|
||||||
|
const functions = [
|
||||||
|
${functionSpecs
|
||||||
|
.map((spec) => ` { exportName: ${stringLiteral(spec.exportName)}, symbol: ${stringLiteral(spec.symbol)}${spec.operation ? `, operation: ${stringLiteral(spec.operation)}` : ""}${spec.field
|
||||||
|
? `, field: { name: ${stringLiteral(spec.field.name)}, type: ${stringLiteral(spec.field.type)}, storage: ${stringLiteral(spec.field.storage)}, conflict: ${stringLiteral(spec.field.conflict)} }`
|
||||||
|
: ""} },`)
|
||||||
|
.join("\n")}
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const ${createObjectName} = (
|
||||||
|
camino: CaminoClient,
|
||||||
|
objectId: string,
|
||||||
|
): ${objectTypeName} => {
|
||||||
|
const id = objectRef<${stringLiteral(classId(schema))}>(objectId) as ${refTypeName};
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
${writableFields
|
||||||
|
.map((fieldSchema) => fieldSchema.conflict === "crdt"
|
||||||
|
? ` ${camel(fieldSchema.name)}: createCrdtField(camino, id, ${stringLiteral(fieldSchema.name)}, ${stringLiteral(crdtStorageTypeId(fieldSchema))}),`
|
||||||
|
: ` ${camel(fieldSchema.name)}: createField(camino, id, ${stringLiteral(fieldSchema.name)}),`)
|
||||||
|
.join("\n")}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const create${className}Context = (
|
||||||
|
camino: CaminoClient,
|
||||||
|
request: InvokeRequest,
|
||||||
|
): ${contextTypeName} => ({
|
||||||
|
object: ${createObjectName}(camino, request.objectId),
|
||||||
|
camino,
|
||||||
|
request,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ${serveName} = (implementation: ${implementationTypeName}) => {
|
||||||
|
serveQuixosPackageRuntime({
|
||||||
|
packageNamespace,
|
||||||
|
packageName,
|
||||||
|
runtimeProtocolVersion,
|
||||||
|
functions,
|
||||||
|
implementation,
|
||||||
|
createContext: create${className}Context,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
const implementationNamed = (schema, name) => schema.implements.find((entry) => entry.interface.namespace === "quixos.react" &&
|
||||||
|
entry.interface.name === name);
|
||||||
|
const requiredBinding = (implementation, name) => {
|
||||||
|
const binding = implementation.typeBindings.find((entry) => entry.name === name);
|
||||||
|
if (!binding) {
|
||||||
|
throw new Error(`${implementation.interface.name} implementation is missing ${name}`);
|
||||||
|
}
|
||||||
|
return binding.type;
|
||||||
|
};
|
||||||
|
const renderReactClient = (schema, root) => {
|
||||||
|
const react = implementationNamed(schema, "ReactComponent");
|
||||||
|
const componentFor = implementationNamed(schema, "ReactComponentFor");
|
||||||
|
if (!react || !componentFor) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const renderProps = requiredBinding(react, "RenderProps");
|
||||||
|
const action = requiredBinding(react, "Action");
|
||||||
|
const object = requiredBinding(componentFor, "Object");
|
||||||
|
if (!object.symbol) {
|
||||||
|
throw new Error(`${schema.id.name}.ReactComponentFor.Object must bind a class symbol`);
|
||||||
|
}
|
||||||
|
const edge = schema.edges.find((candidate) => candidate.sourceField === "for_object");
|
||||||
|
if (!edge) {
|
||||||
|
throw new Error(`${schema.id.name} must declare its ReactComponentFor edge`);
|
||||||
|
}
|
||||||
|
const componentEndpoint = edge.fromEndpoint.projection === "for_object"
|
||||||
|
? edge.fromEndpoint
|
||||||
|
: edge.toEndpoint.projection === "for_object"
|
||||||
|
? edge.toEndpoint
|
||||||
|
: undefined;
|
||||||
|
const objectEndpoint = componentEndpoint === edge.fromEndpoint ? edge.toEndpoint : edge.fromEndpoint;
|
||||||
|
if (!componentEndpoint || !objectEndpoint.projection) {
|
||||||
|
throw new Error(`${schema.id.name}.for_object edge has no inverse projection`);
|
||||||
|
}
|
||||||
|
if (componentEndpoint.cardinality !== "exactly_one") {
|
||||||
|
throw new Error(`${schema.id.name}.for_object must have exactly_one cardinality`);
|
||||||
|
}
|
||||||
|
if (!objectEndpoint.materialization ||
|
||||||
|
symbolId(objectEndpoint.materialization.class) !== symbolId(schema.id)) {
|
||||||
|
throw new Error(`${schema.id.name}.${objectEndpoint.projection} must materialize ${classId(schema)}`);
|
||||||
|
}
|
||||||
|
const className = schema.id.name;
|
||||||
|
const componentName = className.endsWith("ReactComponent")
|
||||||
|
? `${className.slice(0, -"ReactComponent".length)}Component`
|
||||||
|
: className;
|
||||||
|
const propsTypeName = `${componentName}Props`;
|
||||||
|
const renderPropsType = tsTypeForTypeRef(renderProps, root);
|
||||||
|
const actionType = tsTypeForTypeRef(action, root);
|
||||||
|
const objectType = tsTypeForTypeRef(object, root);
|
||||||
|
const declarations = collectProtoDeclarations(root, [renderProps, action]);
|
||||||
|
return `${generatedWarning}import {
|
||||||
|
createReactComponentFor,
|
||||||
|
type ReactComponentHostProps,
|
||||||
|
type ObjectRef,
|
||||||
|
} from "@quixos/camino-react-runtime";
|
||||||
|
|
||||||
|
${declarations}
|
||||||
|
|
||||||
|
export type ${propsTypeName} = {
|
||||||
|
forObject: ${objectType};
|
||||||
|
} & ${renderPropsType} & ReactComponentHostProps<${actionType}>;
|
||||||
|
|
||||||
|
export const ${componentName} = createReactComponentFor<
|
||||||
|
${objectType},
|
||||||
|
${renderPropsType},
|
||||||
|
${actionType}
|
||||||
|
>({
|
||||||
|
componentClassId: ${stringLiteral(classId(schema))},
|
||||||
|
projection: ${stringLiteral(objectEndpoint.projection)},
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
const reactRuntimeDeclaration = `${generatedWarning}declare module "@quixos/camino-react-runtime" {
|
||||||
|
import type * as AutomergeNamespace from "@automerge/automerge";
|
||||||
|
|
||||||
|
export type ObjectRef<ClassId extends string> = string & {
|
||||||
|
readonly $caminoClass: ClassId;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LiveFieldProp<T> = {
|
||||||
|
value: T;
|
||||||
|
source: {
|
||||||
|
objectId: string;
|
||||||
|
fieldName: string;
|
||||||
|
fieldType?: string;
|
||||||
|
fieldStorage?: string;
|
||||||
|
conflictStrategy?: string;
|
||||||
|
revision?: string | number | bigint;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReactComponentHostProps<Action> = {
|
||||||
|
onAction?: (action: Action) => void;
|
||||||
|
fallback?: unknown;
|
||||||
|
className?: string;
|
||||||
|
style?: Record<string, string | number | undefined>;
|
||||||
|
onError?: (error: Error) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReactComponentImplementationProps<CaminoProps, RenderProps, Action> = {
|
||||||
|
camino: CaminoProps;
|
||||||
|
render: RenderProps;
|
||||||
|
dispatch: (action: Action) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createReactComponentFor: <
|
||||||
|
ForObject extends ObjectRef<string>,
|
||||||
|
RenderProps extends object,
|
||||||
|
Action,
|
||||||
|
>(config: {
|
||||||
|
componentClassId: string;
|
||||||
|
projection: string;
|
||||||
|
}) => (props: {
|
||||||
|
forObject: ForObject;
|
||||||
|
} & RenderProps & ReactComponentHostProps<Action>) => any;
|
||||||
|
|
||||||
|
export const callObjectMethod: <Result = unknown>(
|
||||||
|
object: ObjectRef<string>,
|
||||||
|
methodName: string,
|
||||||
|
input?: Record<string, unknown>,
|
||||||
|
) => Promise<Result>;
|
||||||
|
|
||||||
|
export const h: (...args: any[]) => any;
|
||||||
|
export const useLiveField: <T>(
|
||||||
|
field: LiveFieldProp<T>,
|
||||||
|
) => [T, (value: T) => void | Promise<void>];
|
||||||
|
export const Automerge: typeof AutomergeNamespace;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const renderServer = (schema, implementationImport) => {
|
||||||
|
const className = schema.id.name;
|
||||||
|
return `${generatedWarning}import * as implementation from ${stringLiteral(implementationImport)};
|
||||||
|
import { serve${className}Runtime } from "./${schema.id.namespace}.${className}.runtime.js";
|
||||||
|
|
||||||
|
serve${className}Runtime(implementation);
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
const main = async () => {
|
||||||
|
const reactOnly = process.argv[2] === "--react-only";
|
||||||
|
const offset = reactOnly ? 1 : 0;
|
||||||
|
const sourceFile = required(process.argv[2 + offset]);
|
||||||
|
const outDir = required(process.argv[3 + offset]);
|
||||||
|
const implementationImport = process.argv[4 + offset] ?? "../task.impl.js";
|
||||||
|
const compiled = await compileCaminoSchema(sourceFile);
|
||||||
|
const { root } = await loadCaminoSchemaRoot(sourceFile);
|
||||||
|
if (compiled.classes.length !== 1) {
|
||||||
|
throw new Error(`Expected exactly one Camino class in ${sourceFile}, found ${compiled.classes.length}`);
|
||||||
|
}
|
||||||
|
const schema = compiled.classes[0];
|
||||||
|
if (!schema) {
|
||||||
|
throw new Error(`No Camino class found in ${sourceFile}`);
|
||||||
|
}
|
||||||
|
fs.mkdirSync(outDir, { recursive: true });
|
||||||
|
const baseName = `${schema.id.namespace}.${schema.id.name}`;
|
||||||
|
const reactClient = renderReactClient(schema, root);
|
||||||
|
if (!reactOnly) {
|
||||||
|
fs.writeFileSync(path.join(outDir, `${baseName}.runtime.ts`), renderRuntime(schema, root));
|
||||||
|
fs.writeFileSync(path.join(outDir, `${baseName}.server.ts`), renderServer(schema, implementationImport));
|
||||||
|
}
|
||||||
|
if (reactClient) {
|
||||||
|
fs.writeFileSync(path.join(outDir, `${baseName}.react.ts`), reactClient);
|
||||||
|
fs.writeFileSync(path.join(outDir, "camino-react-runtime.d.ts"), reactRuntimeDeclaration);
|
||||||
|
}
|
||||||
|
else if (reactOnly) {
|
||||||
|
throw new Error(`${schema.id.name} does not implement ReactComponentFor`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await main();
|
||||||
Vendored
+110
@@ -0,0 +1,110 @@
|
|||||||
|
import { type Client } from "@connectrpc/connect";
|
||||||
|
import { type CaminoAutomergeDoc } from "@quixos/camino-datatypes/crdt-automerge";
|
||||||
|
import { CaminoService, type Value } from "./camino/api_pb.js";
|
||||||
|
import { type InvokeRequest } from "./quixos/runtime_pb.js";
|
||||||
|
declare const objectRefBrand: unique symbol;
|
||||||
|
export type ObjectRef<ClassId extends string> = string & {
|
||||||
|
readonly [objectRefBrand]: ClassId;
|
||||||
|
};
|
||||||
|
export type CaminoClient = Client<typeof CaminoService>;
|
||||||
|
export type { InvokeRequest, Value };
|
||||||
|
export type DerivedDependency = {
|
||||||
|
kind: "object";
|
||||||
|
objectId: string;
|
||||||
|
} | {
|
||||||
|
kind: "field";
|
||||||
|
objectId: string;
|
||||||
|
fieldName: string;
|
||||||
|
} | {
|
||||||
|
kind: "edges";
|
||||||
|
objectId: string;
|
||||||
|
sourceField?: string;
|
||||||
|
};
|
||||||
|
export declare const recordDerivedDependency: (dependency: DerivedDependency) => void;
|
||||||
|
export declare const getActiveDerivedDependencies: () => DerivedDependency[];
|
||||||
|
export declare const runWithDerivedDependencyTracking: <T>(fn: () => Promise<T> | T) => Promise<{
|
||||||
|
value: T;
|
||||||
|
dependencies: DerivedDependency[];
|
||||||
|
}>;
|
||||||
|
export type Field<T> = {
|
||||||
|
get(): Promise<T | undefined>;
|
||||||
|
set(value: T): Promise<void>;
|
||||||
|
};
|
||||||
|
export type CrdtField<T> = Field<CaminoAutomergeDoc<T>> & {
|
||||||
|
change(fn: (doc: {
|
||||||
|
value: T;
|
||||||
|
}) => void): Promise<CaminoAutomergeDoc<T>>;
|
||||||
|
replaceFromJson(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 declare const liveFieldValue: <T>(params: {
|
||||||
|
value: T;
|
||||||
|
objectId: string;
|
||||||
|
fieldName: string;
|
||||||
|
fieldType?: string;
|
||||||
|
fieldStorage?: string;
|
||||||
|
conflictStrategy?: string;
|
||||||
|
revision?: number | string | bigint;
|
||||||
|
}) => LiveFieldValue<T>;
|
||||||
|
export type RuntimeHandler<Context> = (context: Context) => Promise<unknown> | unknown;
|
||||||
|
export type FieldOperation<Context> = {
|
||||||
|
get?: RuntimeHandler<Context>;
|
||||||
|
set?: RuntimeHandler<Context>;
|
||||||
|
watchStart?: RuntimeHandler<Context>;
|
||||||
|
watchStop?: RuntimeHandler<Context>;
|
||||||
|
};
|
||||||
|
export declare const fieldOps: <Context>(operation: FieldOperation<Context>) => FieldOperation<Context>;
|
||||||
|
export declare const derivedWithDeps: <Context>(operation: {
|
||||||
|
get: RuntimeHandler<Context>;
|
||||||
|
}) => FieldOperation<Context>;
|
||||||
|
export type RuntimeFunctionSpec<ExportName extends string = string> = {
|
||||||
|
exportName: ExportName;
|
||||||
|
symbol: string;
|
||||||
|
operation?: string;
|
||||||
|
field?: {
|
||||||
|
name: string;
|
||||||
|
type?: string;
|
||||||
|
storage?: string;
|
||||||
|
conflict?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
type FieldCodec = {
|
||||||
|
fieldType?: string;
|
||||||
|
conflictStrategy?: string;
|
||||||
|
};
|
||||||
|
export declare const jsToProtoValue: (value: unknown, codec?: FieldCodec) => Value;
|
||||||
|
export declare const protoValueToJs: (value: Value | undefined) => unknown;
|
||||||
|
export declare const jsObjectToProtoFields: (value: Record<string, unknown>) => {
|
||||||
|
[k: string]: Value;
|
||||||
|
};
|
||||||
|
export declare const protoFieldsToJs: (fields: Record<string, Value> | undefined) => Record<string, unknown>;
|
||||||
|
export declare const canonicalFieldParams: (params: Record<string, Value> | undefined) => string;
|
||||||
|
export declare const fieldOperationKey: (params: {
|
||||||
|
objectId: string;
|
||||||
|
fieldName: string;
|
||||||
|
input?: Record<string, Value>;
|
||||||
|
}) => string;
|
||||||
|
export declare const objectRef: <ClassId extends string>(objectId: string) => ObjectRef<ClassId>;
|
||||||
|
export declare const createField: <T>(camino: CaminoClient, objectId: string, fieldName: string, codec?: FieldCodec) => Field<T>;
|
||||||
|
export declare const createCrdtField: <T>(camino: CaminoClient, objectId: string, fieldName: string, fieldType: string) => CrdtField<T>;
|
||||||
|
export declare const serveQuixosPackageRuntime: <Context, Spec extends readonly RuntimeFunctionSpec[]>(params: {
|
||||||
|
packageNamespace: string;
|
||||||
|
packageName: string;
|
||||||
|
runtimeProtocolVersion: string;
|
||||||
|
functions: Spec;
|
||||||
|
implementation: { [Name in Spec[number]["exportName"]]: RuntimeHandler<Context> | FieldOperation<Context>; };
|
||||||
|
createContext: (camino: CaminoClient, request: InvokeRequest) => Context;
|
||||||
|
}) => void;
|
||||||
|
//# sourceMappingURL=index.d.ts.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,MAAM,EAGZ,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,yCAAyC,CAAC;AAKjD,OAAO,EACL,aAAa,EASb,KAAK,KAAK,EACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAOL,KAAK,aAAa,EAGnB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,CAAC,MAAM,cAAc,EAAE,OAAO,MAAM,CAAC;AAE5C,MAAM,MAAM,SAAS,CAAC,OAAO,SAAS,MAAM,IAAI,MAAM,GAAG;IACvD,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAExD,YAAY,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AAErC,MAAM,MAAM,iBAAiB,GACzB;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAgCN,eAAO,MAAM,uBAAuB,GAAI,YAAY,iBAAiB,SAEpE,CAAC;AAEF,eAAO,MAAM,4BAA4B,QAAO,iBAAiB,EACb,CAAC;AAErD,eAAO,MAAM,gCAAgC,GAAU,CAAC,EACtD,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,KACvB,OAAO,CAAC;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,YAAY,EAAE,iBAAiB,EAAE,CAAA;CAAE,CAOzD,CAAC;AAqEF,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI;IACrB,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC9B,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,GAAG;IACxD,MAAM,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,eAAe,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,YAAY,EAAE,CAAC,CAAC;IAChB,aAAa,EAAE;QACb,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM,CAAC;YACjB,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;YAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;SACrC,CAAC;KACH,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,QAAQ;IACxC,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CACrC,KAAG,cAAc,CAAC,CAAC,CAclB,CAAC;AAEH,MAAM,MAAM,cAAc,CAAC,OAAO,IAAI,CACpC,OAAO,EAAE,OAAO,KACb,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;AAEhC,MAAM,MAAM,cAAc,CAAC,OAAO,IAAI;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC9B,GAAG,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,OAAO,EAC9B,WAAW,cAAc,CAAC,OAAO,CAAC,KACjC,cAAc,CAAC,OAAO,CAAc,CAAC;AAExC,eAAO,MAAM,eAAe,GAAI,OAAO,EAAE,WAAW;IAClD,GAAG,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;CAC9B,KAAG,cAAc,CAAC,OAAO,CA0KzB,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,UAAU,SAAS,MAAM,GAAG,MAAM,IAAI;IACpE,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC;AAuDF,KAAK,UAAU,GAAG;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAoDF,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,EAAE,QAAQ,UAAU,KAAG,KAyGnE,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,OAAO,KAAK,GAAG,SAAS,KAAG,OA2BzD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;CAKjE,CAAC;AAEJ,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,SAAS,KACxC,MAAM,CAAC,MAAM,EAAE,OAAO,CAMtB,CAAC;AAiBJ,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,SAAS,WAWxC,CAAC;AAEJ,eAAO,MAAM,iBAAiB,GAAI,QAAQ;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC/B,WAKY,CAAC;AA4Dd,eAAO,MAAM,SAAS,GAAI,OAAO,SAAS,MAAM,EAAE,UAAU,MAAM,KACpD,SAAS,CAAC,OAAO,CAAC,CAAC;AAEjC,eAAO,MAAM,WAAW,GAAI,CAAC,EAC3B,QAAQ,YAAY,EACpB,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,QAAQ,UAAU,KACjB,KAAK,CAAC,CAAC,CAaR,CAAC;AAEH,eAAO,MAAM,eAAe,GAAI,CAAC,EAC/B,QAAQ,YAAY,EACpB,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,WAAW,MAAM,KAChB,SAAS,CAAC,CAAC,CA0Bb,CAAC;AA+CF,eAAO,MAAM,yBAAyB,GACpC,OAAO,EACP,IAAI,SAAS,SAAS,mBAAmB,EAAE,EAC3C,QAAQ;IACR,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,GACb,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,GAC/B,cAAc,CAAC,OAAO,CAAC,GACvB,cAAc,CAAC,OAAO,CAAC,GAC5B,CAAC;IACF,aAAa,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC;CAC1E,SAwNA,CAAC"}
|
||||||
Vendored
+731
@@ -0,0 +1,731 @@
|
|||||||
|
import http from "node:http";
|
||||||
|
import { AsyncLocalStorage } from "node:async_hooks";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { create } from "@bufbuild/protobuf";
|
||||||
|
import { createClient, } from "@connectrpc/connect";
|
||||||
|
import * as Automerge from "@automerge/automerge";
|
||||||
|
import { createAutomergeFieldValue, encodeAutomergeFieldWrite, loadAutomergeFieldDoc, } from "@quixos/camino-datatypes/crdt-automerge";
|
||||||
|
import { connectNodeAdapter, createConnectTransport, } from "@connectrpc/connect-node";
|
||||||
|
import { CaminoService, CrdtValueSchema, FieldValueSourceSchema, ListValueSchema, NullValueSchema, ObjectValueSchema, RefValueSchema, ValueSchema, ValueSourceSchema, } from "./camino/api_pb.js";
|
||||||
|
import { DerivedDependencySchema, HandshakeResponseSchema, InvokeResponseSchema, PackageRuntime, WatchEventSchema, WatchRequestSchema, } from "./quixos/runtime_pb.js";
|
||||||
|
import { FunctionRefSchema } from "./quixos/refs_pb.js";
|
||||||
|
const dependencyKey = (dependency) => {
|
||||||
|
switch (dependency.kind) {
|
||||||
|
case "object":
|
||||||
|
return `object:${dependency.objectId}`;
|
||||||
|
case "field":
|
||||||
|
return `field:${dependency.objectId}:${dependency.fieldName}`;
|
||||||
|
case "edges":
|
||||||
|
return `edges:${dependency.objectId}:${dependency.sourceField ?? ""}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const createDependencyTracker = () => {
|
||||||
|
const dependencies = new Map();
|
||||||
|
return {
|
||||||
|
record(dependency) {
|
||||||
|
dependencies.set(dependencyKey(dependency), dependency);
|
||||||
|
},
|
||||||
|
dependencies() {
|
||||||
|
return [...dependencies.values()];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const dependencyStorage = new AsyncLocalStorage();
|
||||||
|
export const recordDerivedDependency = (dependency) => {
|
||||||
|
dependencyStorage.getStore()?.record(dependency);
|
||||||
|
};
|
||||||
|
export const getActiveDerivedDependencies = () => dependencyStorage.getStore()?.dependencies() ?? [];
|
||||||
|
export const runWithDerivedDependencyTracking = async (fn) => {
|
||||||
|
const tracker = createDependencyTracker();
|
||||||
|
const value = await dependencyStorage.run(tracker, fn);
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
dependencies: tracker.dependencies(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const logDerivedDependencies = (dependencies) => {
|
||||||
|
if (process.env.QUIXOS_DERIVED_DEPS_LOG !== "1") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
process.stderr.write(`[derived-deps] ${JSON.stringify({ dependencies })}\n`);
|
||||||
|
};
|
||||||
|
const logDerivedWatch = (event, detail) => {
|
||||||
|
if (process.env.QUIXOS_DERIVED_WATCH_LOG !== "1") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
process.stderr.write(`[derived-watch] ${JSON.stringify({ event, ...detail })}\n`);
|
||||||
|
};
|
||||||
|
const watchSinkStorage = new AsyncLocalStorage();
|
||||||
|
const dependencyObjectIds = (dependencies) => new Set(dependencies.map((dependency) => dependency.objectId));
|
||||||
|
const stopDerivedWatch = (watch) => {
|
||||||
|
if (watch.stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
watch.stopped = true;
|
||||||
|
for (const controller of watch.controllers.values()) {
|
||||||
|
controller.abort();
|
||||||
|
}
|
||||||
|
watch.controllers.clear();
|
||||||
|
logDerivedWatch("stopped", { watchId: watch.watchId });
|
||||||
|
};
|
||||||
|
export const liveFieldValue = (params) => ({
|
||||||
|
$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 const fieldOps = (operation) => operation;
|
||||||
|
export const derivedWithDeps = (operation) => {
|
||||||
|
const watches = new Map();
|
||||||
|
const rerun = async (watch) => {
|
||||||
|
if (watch.stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (watch.running) {
|
||||||
|
watch.pending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
watch.running = true;
|
||||||
|
try {
|
||||||
|
do {
|
||||||
|
watch.pending = false;
|
||||||
|
const { value, dependencies } = await runWithDerivedDependencyTracking(() => watch.get(watch.context));
|
||||||
|
watch.lastValue = value;
|
||||||
|
logDerivedDependencies(dependencies);
|
||||||
|
logDerivedWatch("evaluated", {
|
||||||
|
watchId: watch.watchId,
|
||||||
|
value,
|
||||||
|
dependencies,
|
||||||
|
});
|
||||||
|
watch.sink?.publish({
|
||||||
|
watchId: watch.watchId,
|
||||||
|
value,
|
||||||
|
dependencies,
|
||||||
|
initial: !watch.emitted,
|
||||||
|
});
|
||||||
|
watch.emitted = true;
|
||||||
|
reconcileSubscriptions(watch, dependencies);
|
||||||
|
} while (watch.pending && !watch.stopped);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
logDerivedWatch("error", {
|
||||||
|
watchId: watch.watchId,
|
||||||
|
error: message,
|
||||||
|
});
|
||||||
|
watch.sink?.publish({
|
||||||
|
watchId: watch.watchId,
|
||||||
|
dependencies: [],
|
||||||
|
error: message,
|
||||||
|
initial: !watch.emitted,
|
||||||
|
});
|
||||||
|
watch.emitted = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
watch.running = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const scheduleRerun = (watch, reason) => {
|
||||||
|
if (watch.stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logDerivedWatch("scheduled", { watchId: watch.watchId, reason });
|
||||||
|
void rerun(watch);
|
||||||
|
};
|
||||||
|
const startObjectWatch = (watch, objectId) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
watch.controllers.set(objectId, controller);
|
||||||
|
logDerivedWatch("subscribed", { watchId: watch.watchId, objectId });
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
for await (const event of watch.context.camino.watchObject({
|
||||||
|
objectId,
|
||||||
|
includeSnapshot: false,
|
||||||
|
}, { signal: controller.signal })) {
|
||||||
|
if (controller.signal.aborted || watch.stopped) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scheduleRerun(watch, event.op?.id ? `op:${event.op.id}` : `object:${objectId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (!controller.signal.aborted && !watch.stopped) {
|
||||||
|
logDerivedWatch("watch-error", {
|
||||||
|
watchId: watch.watchId,
|
||||||
|
objectId,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (watch.controllers.get(objectId) === controller) {
|
||||||
|
watch.controllers.delete(objectId);
|
||||||
|
logDerivedWatch("unsubscribed", { watchId: watch.watchId, objectId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
const reconcileSubscriptions = (watch, dependencies) => {
|
||||||
|
const objectIds = dependencyObjectIds(dependencies);
|
||||||
|
for (const [objectId, controller] of watch.controllers.entries()) {
|
||||||
|
if (!objectIds.has(objectId)) {
|
||||||
|
controller.abort();
|
||||||
|
watch.controllers.delete(objectId);
|
||||||
|
logDerivedWatch("unsubscribed", { watchId: watch.watchId, objectId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const objectId of objectIds) {
|
||||||
|
if (!watch.controllers.has(objectId)) {
|
||||||
|
startObjectWatch(watch, objectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return fieldOps({
|
||||||
|
async get(context) {
|
||||||
|
const { value, dependencies } = await runWithDerivedDependencyTracking(() => operation.get(context));
|
||||||
|
logDerivedDependencies(dependencies);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
async watchStart(context) {
|
||||||
|
const watchId = `watch:${randomUUID()}`;
|
||||||
|
const watch = {
|
||||||
|
watchId,
|
||||||
|
context: context,
|
||||||
|
get: operation.get,
|
||||||
|
sink: watchSinkStorage.getStore(),
|
||||||
|
controllers: new Map(),
|
||||||
|
running: false,
|
||||||
|
pending: false,
|
||||||
|
stopped: false,
|
||||||
|
emitted: false,
|
||||||
|
lastValue: undefined,
|
||||||
|
};
|
||||||
|
watches.set(watchId, watch);
|
||||||
|
logDerivedWatch("started", { watchId });
|
||||||
|
await rerun(watch);
|
||||||
|
return {
|
||||||
|
watch_id: watchId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watchStop(context) {
|
||||||
|
const input = protoFieldsToJs(context.request.input);
|
||||||
|
const watchId = typeof input.watch_id === "string"
|
||||||
|
? input.watch_id
|
||||||
|
: typeof input.watchId === "string"
|
||||||
|
? input.watchId
|
||||||
|
: "";
|
||||||
|
const watch = watches.get(watchId);
|
||||||
|
if (!watch) {
|
||||||
|
throw new Error(`Unknown derived watch: ${watchId}`);
|
||||||
|
}
|
||||||
|
stopDerivedWatch(watch);
|
||||||
|
watches.delete(watchId);
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
class AsyncEventQueue {
|
||||||
|
values = [];
|
||||||
|
waiters = [];
|
||||||
|
closed = false;
|
||||||
|
push(value) {
|
||||||
|
if (this.closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const waiter = this.waiters.shift();
|
||||||
|
if (waiter) {
|
||||||
|
waiter({ value, done: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.values.push(value);
|
||||||
|
}
|
||||||
|
close() {
|
||||||
|
if (this.closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.closed = true;
|
||||||
|
for (const waiter of this.waiters.splice(0)) {
|
||||||
|
waiter({ value: undefined, done: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async next() {
|
||||||
|
const value = this.values.shift();
|
||||||
|
if (value !== undefined) {
|
||||||
|
return { value, done: false };
|
||||||
|
}
|
||||||
|
if (this.closed) {
|
||||||
|
return { value: undefined, done: true };
|
||||||
|
}
|
||||||
|
return await new Promise((resolve) => {
|
||||||
|
this.waiters.push(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
[Symbol.asyncIterator]() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
||||||
|
const bytesToBase64 = (value) => Buffer.from(value).toString("base64");
|
||||||
|
const base64ToBytes = (value) => Buffer.from(value, "base64");
|
||||||
|
const sourceToProto = (value) => {
|
||||||
|
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 codecFromSource = (value) => {
|
||||||
|
if (!isRecord(value) || !isRecord(value.field)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const field = value.field;
|
||||||
|
return {
|
||||||
|
...(typeof field.fieldType === "string" ? { fieldType: field.fieldType } : {}),
|
||||||
|
...(typeof field.conflictStrategy === "string"
|
||||||
|
? { conflictStrategy: field.conflictStrategy }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const withSource = (value, source) => {
|
||||||
|
const sourceValue = sourceToProto(source);
|
||||||
|
return sourceValue ? create(ValueSchema, { ...value, source: sourceValue }) : value;
|
||||||
|
};
|
||||||
|
export const jsToProtoValue = (value, codec) => {
|
||||||
|
if (codec?.conflictStrategy === "crdt" && codec.fieldType) {
|
||||||
|
return jsToProtoValue(encodeAutomergeFieldWrite(value, codec.fieldType));
|
||||||
|
}
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: {
|
||||||
|
case: "nullValue",
|
||||||
|
value: create(NullValueSchema, {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof value === "boolean") {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: { case: "boolValue", value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof value === "number") {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: { case: "numberValue", value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof value === "bigint") {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: { case: "integerValue", value: value.toString() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof value === "string") {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: { case: "stringValue", value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (value instanceof Uint8Array) {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: { case: "bytesValue", value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: {
|
||||||
|
case: "listValue",
|
||||||
|
value: create(ListValueSchema, {
|
||||||
|
values: value.map((entry) => jsToProtoValue(entry)),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isRecord(value)) {
|
||||||
|
if (Object.hasOwn(value, "$caminoValue") && isRecord(value.$caminoSource)) {
|
||||||
|
return withSource(jsToProtoValue(value.$caminoValue, codecFromSource(value.$caminoSource)), value.$caminoSource);
|
||||||
|
}
|
||||||
|
if (typeof value.$caminoRef === "string") {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: {
|
||||||
|
case: "refValue",
|
||||||
|
value: create(RefValueSchema, {
|
||||||
|
objectId: value.$caminoRef,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof value.$caminoBytes === "string" &&
|
||||||
|
Object.keys(value).every((key) => key === "$caminoBytes")) {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: {
|
||||||
|
case: "bytesValue",
|
||||||
|
value: base64ToBytes(value.$caminoBytes),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof value.$caminoCrdtType === "string" &&
|
||||||
|
typeof value.$caminoCrdtPayload === "string") {
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: {
|
||||||
|
case: "crdtValue",
|
||||||
|
value: create(CrdtValueSchema, {
|
||||||
|
type: value.$caminoCrdtType,
|
||||||
|
encoding: typeof value.$caminoCrdtEncoding === "string"
|
||||||
|
? value.$caminoCrdtEncoding
|
||||||
|
: "base64",
|
||||||
|
payload: base64ToBytes(value.$caminoCrdtPayload),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return create(ValueSchema, {
|
||||||
|
kind: {
|
||||||
|
case: "objectValue",
|
||||||
|
value: create(ObjectValueSchema, {
|
||||||
|
fields: jsObjectToProtoFields(value),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new Error(`Unsupported Camino RPC value: ${typeof value}`);
|
||||||
|
};
|
||||||
|
export const protoValueToJs = (value) => {
|
||||||
|
switch (value?.kind.case) {
|
||||||
|
case "nullValue":
|
||||||
|
case undefined:
|
||||||
|
return null;
|
||||||
|
case "boolValue":
|
||||||
|
case "numberValue":
|
||||||
|
case "stringValue":
|
||||||
|
return value.kind.value;
|
||||||
|
case "integerValue":
|
||||||
|
return value.kind.value;
|
||||||
|
case "bytesValue":
|
||||||
|
return bytesToBase64(value.kind.value);
|
||||||
|
case "listValue":
|
||||||
|
return value.kind.value.values.map(protoValueToJs);
|
||||||
|
case "objectValue":
|
||||||
|
return protoFieldsToJs(value.kind.value.fields);
|
||||||
|
case "refValue":
|
||||||
|
return value.kind.value.objectId;
|
||||||
|
case "crdtValue":
|
||||||
|
return loadAutomergeFieldDoc({
|
||||||
|
$caminoCrdtType: value.kind.value.type,
|
||||||
|
$caminoCrdtEncoding: value.kind.value.encoding === "base64" ? "base64" : "base64",
|
||||||
|
$caminoCrdtPayload: bytesToBase64(value.kind.value.payload),
|
||||||
|
}, value.kind.value.type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const jsObjectToProtoFields = (value) => Object.fromEntries(Object.entries(value)
|
||||||
|
.filter(([, fieldValue]) => fieldValue !== undefined)
|
||||||
|
.map(([key, fieldValue]) => [key, jsToProtoValue(fieldValue)]));
|
||||||
|
export const protoFieldsToJs = (fields) => Object.fromEntries(Object.entries(fields ?? {}).map(([key, value]) => [
|
||||||
|
key,
|
||||||
|
protoValueToJs(value),
|
||||||
|
]));
|
||||||
|
const normalizeForCanonicalJson = (value) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map(normalizeForCanonicalJson);
|
||||||
|
}
|
||||||
|
if (!value || typeof value !== "object") {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return Object.fromEntries(Object.entries(value)
|
||||||
|
.filter(([, entryValue]) => entryValue !== undefined)
|
||||||
|
.sort(([left], [right]) => left.localeCompare(right))
|
||||||
|
.map(([key, entryValue]) => [key, normalizeForCanonicalJson(entryValue)]));
|
||||||
|
};
|
||||||
|
export const canonicalFieldParams = (params) => JSON.stringify(normalizeForCanonicalJson(Object.fromEntries(Object.entries(params ?? {}).map(([key, value]) => [
|
||||||
|
key,
|
||||||
|
protoValueToJs(value),
|
||||||
|
]))));
|
||||||
|
export const fieldOperationKey = (params) => [
|
||||||
|
params.objectId,
|
||||||
|
params.fieldName,
|
||||||
|
Buffer.from(canonicalFieldParams(params.input)).toString("base64url"),
|
||||||
|
].join(":");
|
||||||
|
const derivedDependencyToProto = (dependency) => create(DerivedDependencySchema, {
|
||||||
|
kind: dependency.kind,
|
||||||
|
objectId: dependency.objectId,
|
||||||
|
fieldName: dependency.kind === "field" ? dependency.fieldName : "",
|
||||||
|
sourceField: dependency.kind === "edges" ? dependency.sourceField ?? "" : "",
|
||||||
|
});
|
||||||
|
const fieldSourceForSpec = (spec, objectId) => {
|
||||||
|
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, objectId, value) => {
|
||||||
|
const source = fieldSourceForSpec(spec, objectId);
|
||||||
|
return source ? { $caminoValue: value, $caminoSource: source } : value;
|
||||||
|
};
|
||||||
|
const derivedEmissionToProtoForSpec = (spec, objectId, event) => create(WatchEventSchema, {
|
||||||
|
watchId: event.watchId,
|
||||||
|
value: jsToProtoValue(event.error ? null : wrapFieldResult(spec, objectId, event.value ?? null)),
|
||||||
|
dependencies: event.dependencies.map(derivedDependencyToProto),
|
||||||
|
error: event.error ?? "",
|
||||||
|
initial: event.initial,
|
||||||
|
});
|
||||||
|
const watchIdFromResult = (value) => {
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const watchId = value.watch_id ?? value.watchId;
|
||||||
|
return typeof watchId === "string" ? watchId : "";
|
||||||
|
};
|
||||||
|
export const objectRef = (objectId) => objectId;
|
||||||
|
export const createField = (camino, objectId, fieldName, codec) => ({
|
||||||
|
async get() {
|
||||||
|
recordDerivedDependency({ kind: "field", objectId, fieldName });
|
||||||
|
const response = await camino.getObject({ objectId });
|
||||||
|
return protoValueToJs(response.object?.fields[fieldName]);
|
||||||
|
},
|
||||||
|
async set(value) {
|
||||||
|
await camino.setField({
|
||||||
|
objectId,
|
||||||
|
fieldName,
|
||||||
|
value: jsToProtoValue(value, codec),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const createCrdtField = (camino, objectId, fieldName, fieldType) => {
|
||||||
|
const field = createField(camino, objectId, fieldName, {
|
||||||
|
fieldType,
|
||||||
|
conflictStrategy: "crdt",
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
async change(fn) {
|
||||||
|
const current = await field.get();
|
||||||
|
if (!current) {
|
||||||
|
throw new Error(`Cannot change missing CRDT field ${fieldName}`);
|
||||||
|
}
|
||||||
|
const next = Automerge.change(current, (doc) => {
|
||||||
|
fn(doc);
|
||||||
|
});
|
||||||
|
await field.set(next);
|
||||||
|
return next;
|
||||||
|
},
|
||||||
|
async replaceFromJson(value) {
|
||||||
|
await camino.setField({
|
||||||
|
objectId,
|
||||||
|
fieldName,
|
||||||
|
value: jsToProtoValue(createAutomergeFieldValue(value, fieldType)),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const createDependencyTrackingCaminoClient = (camino) => new Proxy(camino, {
|
||||||
|
get(target, property, receiver) {
|
||||||
|
if (property === "getObject") {
|
||||||
|
return async (request, ...args) => {
|
||||||
|
if (request.objectId) {
|
||||||
|
recordDerivedDependency({
|
||||||
|
kind: "object",
|
||||||
|
objectId: request.objectId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return await Reflect.get(target, property, receiver)(request, ...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (property === "listEdges") {
|
||||||
|
return async (request, ...args) => {
|
||||||
|
if (request.objectId) {
|
||||||
|
recordDerivedDependency({
|
||||||
|
kind: "edges",
|
||||||
|
objectId: request.objectId,
|
||||||
|
...(request.sourceField ? { sourceField: request.sourceField } : {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return await Reflect.get(target, property, receiver)(request, ...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Reflect.get(target, property, receiver);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const serveQuixosPackageRuntime = (params) => {
|
||||||
|
const host = process.env.QUIXOS_RUNTIME_HOST ?? "127.0.0.1";
|
||||||
|
const port = Number(process.env.QUIXOS_RUNTIME_PORT ?? "0");
|
||||||
|
const caminoUrl = process.env.CAMINO_URL ?? "http://127.0.0.1:7310";
|
||||||
|
const runtimeToken = process.env.CAMINO_RUNTIME_AUTH_TOKEN ?? "";
|
||||||
|
if (process.env.CAMINO_RUNTIME_AUTH_REQUIRED === "1" && !runtimeToken) {
|
||||||
|
throw new Error("CAMINO_RUNTIME_AUTH_REQUIRED=1 but CAMINO_RUNTIME_AUTH_TOKEN is missing");
|
||||||
|
}
|
||||||
|
const runtimeTokenInterceptor = (next) => async (request) => {
|
||||||
|
if (runtimeToken) {
|
||||||
|
request.header.set("x-camino-runtime-token", runtimeToken);
|
||||||
|
}
|
||||||
|
return await next(request);
|
||||||
|
};
|
||||||
|
const camino = createClient(CaminoService, createConnectTransport({
|
||||||
|
baseUrl: caminoUrl,
|
||||||
|
httpVersion: "1.1",
|
||||||
|
interceptors: runtimeToken ? [runtimeTokenInterceptor] : [],
|
||||||
|
}));
|
||||||
|
const trackingCamino = createDependencyTrackingCaminoClient(camino);
|
||||||
|
if (!Number.isInteger(port) || port <= 0) {
|
||||||
|
throw new Error("QUIXOS_RUNTIME_PORT must be a positive integer");
|
||||||
|
}
|
||||||
|
const functionRefs = params.functions.map((entry) => create(FunctionRefSchema, {
|
||||||
|
packageNamespace: params.packageNamespace,
|
||||||
|
packageName: params.packageName,
|
||||||
|
symbol: entry.symbol,
|
||||||
|
operation: entry.operation ?? "",
|
||||||
|
}));
|
||||||
|
const resolveRuntimeFunction = (fn) => {
|
||||||
|
const symbol = fn?.symbol ?? "";
|
||||||
|
const spec = params.functions.find((entry) => entry.symbol === symbol &&
|
||||||
|
(entry.operation ?? "") === (fn?.operation ?? ""));
|
||||||
|
if (!spec) {
|
||||||
|
throw new Error(`Unknown ${params.packageName} function: ${symbol}${fn?.operation ? `.${fn.operation}` : ""}`);
|
||||||
|
}
|
||||||
|
const implementationExport = params.implementation[spec.exportName];
|
||||||
|
const runtimeFunction = spec.operation && typeof implementationExport === "object"
|
||||||
|
? implementationExport[spec.operation]
|
||||||
|
: implementationExport;
|
||||||
|
if (!runtimeFunction) {
|
||||||
|
throw new Error(`Missing ${params.packageName} implementation export: ${spec.exportName}${spec.operation ? `.${spec.operation}` : ""}`);
|
||||||
|
}
|
||||||
|
if (typeof runtimeFunction !== "function") {
|
||||||
|
throw new Error(`${params.packageName} implementation export is not callable: ${spec.exportName}${spec.operation ? `.${spec.operation}` : ""}`);
|
||||||
|
}
|
||||||
|
return { spec, runtimeFunction };
|
||||||
|
};
|
||||||
|
const handler = connectNodeAdapter({
|
||||||
|
routes: (router) => {
|
||||||
|
router.service(PackageRuntime, {
|
||||||
|
async handshake() {
|
||||||
|
return create(HandshakeResponseSchema, {
|
||||||
|
packageNamespace: params.packageNamespace,
|
||||||
|
packageName: params.packageName,
|
||||||
|
runtimeProtocolVersion: params.runtimeProtocolVersion,
|
||||||
|
functions: functionRefs,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async invoke(request) {
|
||||||
|
try {
|
||||||
|
const { spec, runtimeFunction } = resolveRuntimeFunction(request.function);
|
||||||
|
const value = await runtimeFunction(params.createContext(trackingCamino, request));
|
||||||
|
return create(InvokeResponseSchema, {
|
||||||
|
ok: true,
|
||||||
|
result: jsToProtoValue(spec.operation === "get"
|
||||||
|
? wrapFieldResult(spec, request.objectId, value)
|
||||||
|
: value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return create(InvokeResponseSchema, {
|
||||||
|
ok: false,
|
||||||
|
result: jsToProtoValue(null),
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async *watch(request, context) {
|
||||||
|
const queue = new AsyncEventQueue();
|
||||||
|
let watchId = "";
|
||||||
|
const closeQueue = () => queue.close();
|
||||||
|
context.signal.addEventListener("abort", closeQueue, { once: true });
|
||||||
|
try {
|
||||||
|
const { spec, runtimeFunction } = resolveRuntimeFunction(request.function);
|
||||||
|
if (spec.operation !== "watchStart") {
|
||||||
|
throw new Error(`PackageRuntime.watch requires a watchStart operation, got ${spec.symbol}${spec.operation ? `.${spec.operation}` : ""}`);
|
||||||
|
}
|
||||||
|
const sink = {
|
||||||
|
publish(event) {
|
||||||
|
queue.push(derivedEmissionToProtoForSpec(spec, request.objectId, event));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const result = await watchSinkStorage.run(sink, async () => runtimeFunction(params.createContext(trackingCamino, request)));
|
||||||
|
watchId = watchIdFromResult(result);
|
||||||
|
if (!watchId) {
|
||||||
|
throw new Error(`Watch start did not return a watch_id for ${spec.symbol}`);
|
||||||
|
}
|
||||||
|
for await (const event of queue) {
|
||||||
|
yield event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
yield create(WatchEventSchema, {
|
||||||
|
watchId,
|
||||||
|
value: jsToProtoValue(null),
|
||||||
|
dependencies: [],
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
initial: !watchId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
context.signal.removeEventListener("abort", closeQueue);
|
||||||
|
queue.close();
|
||||||
|
if (watchId) {
|
||||||
|
try {
|
||||||
|
const stopFunction = create(FunctionRefSchema, {
|
||||||
|
...(request.function ?? create(FunctionRefSchema, {})),
|
||||||
|
operation: "watchStop",
|
||||||
|
});
|
||||||
|
const { runtimeFunction } = resolveRuntimeFunction(stopFunction);
|
||||||
|
await runtimeFunction(params.createContext(trackingCamino, create(WatchRequestSchema, {
|
||||||
|
...request,
|
||||||
|
function: stopFunction,
|
||||||
|
input: {
|
||||||
|
...request.input,
|
||||||
|
watch_id: jsToProtoValue(watchId),
|
||||||
|
},
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
logDerivedWatch("stop-error", {
|
||||||
|
watchId,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const server = http.createServer((request, response) => {
|
||||||
|
void handler(request, response);
|
||||||
|
});
|
||||||
|
const close = () => server.close(() => process.exit(0));
|
||||||
|
server.listen(port, host, () => {
|
||||||
|
process.stderr.write(`${params.packageName} listening on ${host}:${port}\n`);
|
||||||
|
});
|
||||||
|
process.on("SIGTERM", close);
|
||||||
|
process.on("SIGINT", close);
|
||||||
|
};
|
||||||
Vendored
+37
@@ -0,0 +1,37 @@
|
|||||||
|
import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
|
||||||
|
import type { Message } from "@bufbuild/protobuf";
|
||||||
|
/**
|
||||||
|
* Describes the file quixos/refs.proto.
|
||||||
|
*/
|
||||||
|
export declare const file_quixos_refs: GenFile;
|
||||||
|
/**
|
||||||
|
* @generated from message quixos.FunctionRef
|
||||||
|
*/
|
||||||
|
export type FunctionRef = Message<"quixos.FunctionRef"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string package_namespace = 1;
|
||||||
|
*/
|
||||||
|
packageNamespace: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string package_name = 2;
|
||||||
|
*/
|
||||||
|
packageName: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string symbol = 3;
|
||||||
|
*/
|
||||||
|
symbol: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string version_ref = 4;
|
||||||
|
*/
|
||||||
|
versionRef: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string operation = 5;
|
||||||
|
*/
|
||||||
|
operation: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.FunctionRef.
|
||||||
|
* Use `create(FunctionRefSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const FunctionRefSchema: GenMessage<FunctionRef>;
|
||||||
|
//# sourceMappingURL=refs_pb.d.ts.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"refs_pb.d.ts","sourceRoot":"","sources":["../../src/quixos/refs_pb.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAExE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,OAC8L,CAAC;AAE9N;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG;IACxD;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,UAAU,CAAC,WAAW,CACpB,CAAC"}
|
||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// @generated by protoc-gen-es v2.12.1 with parameter "target=ts,import_extension=js"
|
||||||
|
// @generated from file quixos/refs.proto (package quixos, syntax proto3)
|
||||||
|
/* eslint-disable */
|
||||||
|
import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
|
||||||
|
/**
|
||||||
|
* Describes the file quixos/refs.proto.
|
||||||
|
*/
|
||||||
|
export const file_quixos_refs = /*@__PURE__*/ fileDesc("ChFxdWl4b3MvcmVmcy5wcm90bxIGcXVpeG9zInYKC0Z1bmN0aW9uUmVmEhkKEXBhY2thZ2VfbmFtZXNwYWNlGAEgASgJEhQKDHBhY2thZ2VfbmFtZRgCIAEoCRIOCgZzeW1ib2wYAyABKAkSEwoLdmVyc2lvbl9yZWYYBCABKAkSEQoJb3BlcmF0aW9uGAUgASgJYgZwcm90bzM");
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.FunctionRef.
|
||||||
|
* Use `create(FunctionRefSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const FunctionRefSchema = /*@__PURE__*/ messageDesc(file_quixos_refs, 0);
|
||||||
Vendored
+212
@@ -0,0 +1,212 @@
|
|||||||
|
import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2";
|
||||||
|
import type { Value } from "../camino/api_pb.js";
|
||||||
|
import type { FunctionRef } from "./refs_pb.js";
|
||||||
|
import type { Message } from "@bufbuild/protobuf";
|
||||||
|
/**
|
||||||
|
* Describes the file quixos/runtime.proto.
|
||||||
|
*/
|
||||||
|
export declare const file_quixos_runtime: GenFile;
|
||||||
|
/**
|
||||||
|
* @generated from message quixos.runtime.HandshakeRequest
|
||||||
|
*/
|
||||||
|
export type HandshakeRequest = Message<"quixos.runtime.HandshakeRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string orch_protocol_version = 1;
|
||||||
|
*/
|
||||||
|
orchProtocolVersion: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.HandshakeRequest.
|
||||||
|
* Use `create(HandshakeRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const HandshakeRequestSchema: GenMessage<HandshakeRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message quixos.runtime.HandshakeResponse
|
||||||
|
*/
|
||||||
|
export type HandshakeResponse = Message<"quixos.runtime.HandshakeResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string package_namespace = 1;
|
||||||
|
*/
|
||||||
|
packageNamespace: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string package_name = 2;
|
||||||
|
*/
|
||||||
|
packageName: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string runtime_protocol_version = 3;
|
||||||
|
*/
|
||||||
|
runtimeProtocolVersion: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated quixos.FunctionRef functions = 4;
|
||||||
|
*/
|
||||||
|
functions: FunctionRef[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.HandshakeResponse.
|
||||||
|
* Use `create(HandshakeResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const HandshakeResponseSchema: GenMessage<HandshakeResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message quixos.runtime.InvokeRequest
|
||||||
|
*/
|
||||||
|
export type InvokeRequest = Message<"quixos.runtime.InvokeRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string invocation_id = 1;
|
||||||
|
*/
|
||||||
|
invocationId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: quixos.FunctionRef function = 2;
|
||||||
|
*/
|
||||||
|
function?: FunctionRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 3;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, camino.Value> input = 4;
|
||||||
|
*/
|
||||||
|
input: {
|
||||||
|
[key: string]: Value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.InvokeRequest.
|
||||||
|
* Use `create(InvokeRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const InvokeRequestSchema: GenMessage<InvokeRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message quixos.runtime.InvokeResponse
|
||||||
|
*/
|
||||||
|
export type InvokeResponse = Message<"quixos.runtime.InvokeResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: bool ok = 1;
|
||||||
|
*/
|
||||||
|
ok: boolean;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.Value result = 2;
|
||||||
|
*/
|
||||||
|
result?: Value | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string error = 3;
|
||||||
|
*/
|
||||||
|
error: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.InvokeResponse.
|
||||||
|
* Use `create(InvokeResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const InvokeResponseSchema: GenMessage<InvokeResponse>;
|
||||||
|
/**
|
||||||
|
* @generated from message quixos.runtime.WatchRequest
|
||||||
|
*/
|
||||||
|
export type WatchRequest = Message<"quixos.runtime.WatchRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string invocation_id = 1;
|
||||||
|
*/
|
||||||
|
invocationId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: quixos.FunctionRef function = 2;
|
||||||
|
*/
|
||||||
|
function?: FunctionRef | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 3;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: map<string, camino.Value> input = 4;
|
||||||
|
*/
|
||||||
|
input: {
|
||||||
|
[key: string]: Value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.WatchRequest.
|
||||||
|
* Use `create(WatchRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const WatchRequestSchema: GenMessage<WatchRequest>;
|
||||||
|
/**
|
||||||
|
* @generated from message quixos.runtime.DerivedDependency
|
||||||
|
*/
|
||||||
|
export type DerivedDependency = Message<"quixos.runtime.DerivedDependency"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string kind = 1;
|
||||||
|
*/
|
||||||
|
kind: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string object_id = 2;
|
||||||
|
*/
|
||||||
|
objectId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string field_name = 3;
|
||||||
|
*/
|
||||||
|
fieldName: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: string source_field = 4;
|
||||||
|
*/
|
||||||
|
sourceField: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.DerivedDependency.
|
||||||
|
* Use `create(DerivedDependencySchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const DerivedDependencySchema: GenMessage<DerivedDependency>;
|
||||||
|
/**
|
||||||
|
* @generated from message quixos.runtime.WatchEvent
|
||||||
|
*/
|
||||||
|
export type WatchEvent = Message<"quixos.runtime.WatchEvent"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string watch_id = 1;
|
||||||
|
*/
|
||||||
|
watchId: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: camino.Value value = 2;
|
||||||
|
*/
|
||||||
|
value?: Value | undefined;
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated quixos.runtime.DerivedDependency dependencies = 3;
|
||||||
|
*/
|
||||||
|
dependencies: DerivedDependency[];
|
||||||
|
/**
|
||||||
|
* @generated from field: string error = 4;
|
||||||
|
*/
|
||||||
|
error: string;
|
||||||
|
/**
|
||||||
|
* @generated from field: bool initial = 5;
|
||||||
|
*/
|
||||||
|
initial: boolean;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.WatchEvent.
|
||||||
|
* Use `create(WatchEventSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const WatchEventSchema: GenMessage<WatchEvent>;
|
||||||
|
/**
|
||||||
|
* @generated from service quixos.runtime.PackageRuntime
|
||||||
|
*/
|
||||||
|
export declare const PackageRuntime: GenService<{
|
||||||
|
/**
|
||||||
|
* @generated from rpc quixos.runtime.PackageRuntime.Handshake
|
||||||
|
*/
|
||||||
|
handshake: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof HandshakeRequestSchema;
|
||||||
|
output: typeof HandshakeResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc quixos.runtime.PackageRuntime.Invoke
|
||||||
|
*/
|
||||||
|
invoke: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof InvokeRequestSchema;
|
||||||
|
output: typeof InvokeResponseSchema;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @generated from rpc quixos.runtime.PackageRuntime.Watch
|
||||||
|
*/
|
||||||
|
watch: {
|
||||||
|
methodKind: "server_streaming";
|
||||||
|
input: typeof WatchRequestSchema;
|
||||||
|
output: typeof WatchEventSchema;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
//# sourceMappingURL=runtime_pb.d.ts.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"runtime_pb.d.ts","sourceRoot":"","sources":["../../src/quixos/runtime_pb.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAEpF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,OACwoD,CAAC;AAE3qD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,iCAAiC,CAAC,GAAG;IAC1E;;OAEG;IACH,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,UAAU,CAAC,gBAAgB,CAC3B,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,kCAAkC,CAAC,GAAG;IAC5E;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAE/B;;OAEG;IACH,SAAS,EAAE,WAAW,EAAE,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,UAAU,CAAC,iBAAiB,CAC7B,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,8BAA8B,CAAC,GAAG;IACpE;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAEnC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAA;KAAE,CAAC;CACjC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,UAAU,CAAC,aAAa,CACrB,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,+BAA+B,CAAC,GAAG;IACtE;;OAEG;IACH,EAAE,EAAE,OAAO,CAAC;IAEZ;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAE3B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,UAAU,CAAC,cAAc,CACvB,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,6BAA6B,CAAC,GAAG;IAClE;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAEnC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAA;KAAE,CAAC;CACjC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,UAAU,CAAC,YAAY,CACnB,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,kCAAkC,CAAC,GAAG;IAC5E;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,UAAU,CAAC,iBAAiB,CAC7B,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,2BAA2B,CAAC,GAAG;IAC9D;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAE1B;;OAEG;IACH,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAElC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,UAAU,CAAC,UAAU,CACf,CAAC;AAEtC;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,UAAU,CAAC;IACtC;;OAEG;IACH,SAAS,EAAE;QACT,UAAU,EAAE,OAAO,CAAC;QACpB,KAAK,EAAE,OAAO,sBAAsB,CAAC;QACrC,MAAM,EAAE,OAAO,uBAAuB,CAAC;KACxC,CAAC;IACF;;OAEG;IACH,MAAM,EAAE;QACN,UAAU,EAAE,OAAO,CAAC;QACpB,KAAK,EAAE,OAAO,mBAAmB,CAAC;QAClC,MAAM,EAAE,OAAO,oBAAoB,CAAC;KACrC,CAAC;IACF;;OAEG;IACH,KAAK,EAAE;QACL,UAAU,EAAE,kBAAkB,CAAC;QAC/B,KAAK,EAAE,OAAO,kBAAkB,CAAC;QACjC,MAAM,EAAE,OAAO,gBAAgB,CAAC;KACjC,CAAC;CACH,CACoC,CAAC"}
|
||||||
Vendored
+49
@@ -0,0 +1,49 @@
|
|||||||
|
// @generated by protoc-gen-es v2.12.1 with parameter "target=ts,import_extension=js"
|
||||||
|
// @generated from file quixos/runtime.proto (package quixos.runtime, syntax proto3)
|
||||||
|
/* eslint-disable */
|
||||||
|
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
|
||||||
|
import { file_camino_api } from "../camino/api_pb.js";
|
||||||
|
import { file_quixos_refs } from "./refs_pb.js";
|
||||||
|
/**
|
||||||
|
* Describes the file quixos/runtime.proto.
|
||||||
|
*/
|
||||||
|
export const file_quixos_runtime = /*@__PURE__*/ fileDesc("ChRxdWl4b3MvcnVudGltZS5wcm90bxIOcXVpeG9zLnJ1bnRpbWUiMQoQSGFuZHNoYWtlUmVxdWVzdBIdChVvcmNoX3Byb3RvY29sX3ZlcnNpb24YASABKAkijgEKEUhhbmRzaGFrZVJlc3BvbnNlEhkKEXBhY2thZ2VfbmFtZXNwYWNlGAEgASgJEhQKDHBhY2thZ2VfbmFtZRgCIAEoCRIgChhydW50aW1lX3Byb3RvY29sX3ZlcnNpb24YAyABKAkSJgoJZnVuY3Rpb25zGAQgAygLMhMucXVpeG9zLkZ1bmN0aW9uUmVmItYBCg1JbnZva2VSZXF1ZXN0EhUKDWludm9jYXRpb25faWQYASABKAkSJQoIZnVuY3Rpb24YAiABKAsyEy5xdWl4b3MuRnVuY3Rpb25SZWYSEQoJb2JqZWN0X2lkGAMgASgJEjcKBWlucHV0GAQgAygLMigucXVpeG9zLnJ1bnRpbWUuSW52b2tlUmVxdWVzdC5JbnB1dEVudHJ5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASJKCg5JbnZva2VSZXNwb25zZRIKCgJvaxgBIAEoCBIdCgZyZXN1bHQYAiABKAsyDS5jYW1pbm8uVmFsdWUSDQoFZXJyb3IYAyABKAki1AEKDFdhdGNoUmVxdWVzdBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEiUKCGZ1bmN0aW9uGAIgASgLMhMucXVpeG9zLkZ1bmN0aW9uUmVmEhEKCW9iamVjdF9pZBgDIAEoCRI2CgVpbnB1dBgEIAMoCzInLnF1aXhvcy5ydW50aW1lLldhdGNoUmVxdWVzdC5JbnB1dEVudHJ5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASJeChFEZXJpdmVkRGVwZW5kZW5jeRIMCgRraW5kGAEgASgJEhEKCW9iamVjdF9pZBgCIAEoCRISCgpmaWVsZF9uYW1lGAMgASgJEhQKDHNvdXJjZV9maWVsZBgEIAEoCSKVAQoKV2F0Y2hFdmVudBIQCgh3YXRjaF9pZBgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZRI3CgxkZXBlbmRlbmNpZXMYAyADKAsyIS5xdWl4b3MucnVudGltZS5EZXJpdmVkRGVwZW5kZW5jeRINCgVlcnJvchgEIAEoCRIPCgdpbml0aWFsGAUgASgIMvABCg5QYWNrYWdlUnVudGltZRJQCglIYW5kc2hha2USIC5xdWl4b3MucnVudGltZS5IYW5kc2hha2VSZXF1ZXN0GiEucXVpeG9zLnJ1bnRpbWUuSGFuZHNoYWtlUmVzcG9uc2USRwoGSW52b2tlEh0ucXVpeG9zLnJ1bnRpbWUuSW52b2tlUmVxdWVzdBoeLnF1aXhvcy5ydW50aW1lLkludm9rZVJlc3BvbnNlEkMKBVdhdGNoEhwucXVpeG9zLnJ1bnRpbWUuV2F0Y2hSZXF1ZXN0GhoucXVpeG9zLnJ1bnRpbWUuV2F0Y2hFdmVudDABYgZwcm90bzM", [file_camino_api, file_quixos_refs]);
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.HandshakeRequest.
|
||||||
|
* Use `create(HandshakeRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const HandshakeRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_runtime, 0);
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.HandshakeResponse.
|
||||||
|
* Use `create(HandshakeResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const HandshakeResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_runtime, 1);
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.InvokeRequest.
|
||||||
|
* Use `create(InvokeRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const InvokeRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_runtime, 2);
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.InvokeResponse.
|
||||||
|
* Use `create(InvokeResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const InvokeResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_runtime, 3);
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.WatchRequest.
|
||||||
|
* Use `create(WatchRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const WatchRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_runtime, 4);
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.DerivedDependency.
|
||||||
|
* Use `create(DerivedDependencySchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const DerivedDependencySchema = /*@__PURE__*/ messageDesc(file_quixos_runtime, 5);
|
||||||
|
/**
|
||||||
|
* Describes the message quixos.runtime.WatchEvent.
|
||||||
|
* Use `create(WatchEventSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const WatchEventSchema = /*@__PURE__*/ messageDesc(file_quixos_runtime, 6);
|
||||||
|
/**
|
||||||
|
* @generated from service quixos.runtime.PackageRuntime
|
||||||
|
*/
|
||||||
|
export const PackageRuntime = /*@__PURE__*/ serviceDesc(file_quixos_runtime, 0);
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
import protobuf from "protobufjs";
|
||||||
|
import type { SchemaCompileResult } from "./schema-ir.js";
|
||||||
|
export declare const loadCaminoSchemaRoot: (sourceFile: string) => Promise<{
|
||||||
|
absoluteSourceFile: string;
|
||||||
|
root: protobuf.Root;
|
||||||
|
}>;
|
||||||
|
export declare const compileCaminoSchema: (sourceFile: string) => Promise<SchemaCompileResult>;
|
||||||
|
//# sourceMappingURL=schema-compiler.d.ts.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"schema-compiler.d.ts","sourceRoot":"","sources":["../src/schema-compiler.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,EAeV,mBAAmB,EAIpB,MAAM,gBAAgB,CAAC;AA02BxB,eAAO,MAAM,oBAAoB,GAC/B,YAAY,MAAM,KACjB,OAAO,CAAC;IAAE,kBAAkB,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAA;CAAE,CAqC7D,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,YAAY,MAAM,KACjB,OAAO,CAAC,mBAAmB,CAsC7B,CAAC"}
|
||||||
Vendored
+721
@@ -0,0 +1,721 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import protobuf from "protobufjs";
|
||||||
|
const CLASS_OPTION = "(camino.class)";
|
||||||
|
const INTERFACE_OPTION = "(camino.interface)";
|
||||||
|
const IMPLEMENTS_OPTION = "(camino.implements)";
|
||||||
|
const CONSTRUCTOR_OPTION = "(camino.constructor)";
|
||||||
|
const METHOD_OPTION = "(camino.method)";
|
||||||
|
const MIGRATION_OPTION = "(camino.migration)";
|
||||||
|
const CONFLICT_OPTION = "(camino.conflict)";
|
||||||
|
const EDGE_OPTION = "(camino.edge)";
|
||||||
|
const FIELD_STORAGE_OPTION = "(camino.field_storage)";
|
||||||
|
const FIELD_OPS_OPTION = "(camino.field_ops)";
|
||||||
|
const INTERFACE_FIELD_OPTION = "(camino.interface_field)";
|
||||||
|
const DISPLAY_LABEL_OPTION = "(camino.display_label)";
|
||||||
|
const IMPL_OPTION = "(camino.impl)";
|
||||||
|
const SCHEMA_NAMESPACE_OPTION = "(camino.schema_namespace)";
|
||||||
|
const SCHEMA_VERSION_OPTION = "(camino.schema_version)";
|
||||||
|
const enumName = (value) => String(value)
|
||||||
|
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
||||||
|
.replace(/-/g, "_")
|
||||||
|
.toUpperCase();
|
||||||
|
const lowerEnumName = (value) => enumName(value).toLowerCase();
|
||||||
|
const getOption = (options, name) => {
|
||||||
|
if (!options) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (Object.hasOwn(options, name)) {
|
||||||
|
return options[name];
|
||||||
|
}
|
||||||
|
const bareName = name.replace(/^\((.*)\)$/, "$1");
|
||||||
|
if (Object.hasOwn(options, bareName)) {
|
||||||
|
return options[bareName];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
const getParsedOptions = (reflection) => {
|
||||||
|
const parsedOptions = reflection
|
||||||
|
.parsedOptions;
|
||||||
|
return Array.isArray(parsedOptions)
|
||||||
|
? parsedOptions.filter((option) => Boolean(option) && typeof option === "object" && !Array.isArray(option))
|
||||||
|
: [];
|
||||||
|
};
|
||||||
|
const getReflectionOptions = (reflection, name) => {
|
||||||
|
const parsedValues = getParsedOptions(reflection).flatMap((option) => {
|
||||||
|
const value = getOption(option, name);
|
||||||
|
return value === undefined ? [] : [value];
|
||||||
|
});
|
||||||
|
if (parsedValues.length > 0) {
|
||||||
|
return parsedValues;
|
||||||
|
}
|
||||||
|
const value = getOption(reflection.options, name);
|
||||||
|
return value === undefined ? [] : [value];
|
||||||
|
};
|
||||||
|
const getReflectionOption = (reflection, name) => getReflectionOptions(reflection, name)[0];
|
||||||
|
const asObject = (value) => value && typeof value === "object" && !Array.isArray(value)
|
||||||
|
? value
|
||||||
|
: undefined;
|
||||||
|
const asArray = (value) => Array.isArray(value) ? value : value === undefined ? [] : [value];
|
||||||
|
const readString = (object, key) => {
|
||||||
|
const value = object?.[key];
|
||||||
|
return typeof value === "string" ? value : undefined;
|
||||||
|
};
|
||||||
|
const readNumber = (object, key) => {
|
||||||
|
const value = object?.[key];
|
||||||
|
return typeof value === "number" && Number.isFinite(value)
|
||||||
|
? value
|
||||||
|
: undefined;
|
||||||
|
};
|
||||||
|
const readBoolean = (object, key) => {
|
||||||
|
const value = object?.[key];
|
||||||
|
return typeof value === "boolean" ? value : undefined;
|
||||||
|
};
|
||||||
|
const symbolRefFromOption = (value, fallback) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
namespace: readString(object, "namespace") ?? fallback.namespace,
|
||||||
|
name: readString(object, "name") ?? fallback.name,
|
||||||
|
version: readString(object, "version") ?? fallback.version,
|
||||||
|
...(readString(object, "hash")
|
||||||
|
? { hash: readString(object, "hash") }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const typeRefFromOption = (value, fallbackVersion) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const symbol = asObject(object.symbol)
|
||||||
|
? symbolRefFromOption(object.symbol, {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: fallbackVersion,
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
const protoType = readString(object, "proto_type")?.replace(/^\./, "");
|
||||||
|
const symbolProtoType = symbol?.namespace && symbol.name ? `${symbol.namespace}.${symbol.name}` : "";
|
||||||
|
const resolvedProtoType = protoType ?? symbolProtoType;
|
||||||
|
if (!resolvedProtoType) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
protoType: resolvedProtoType,
|
||||||
|
...(symbol?.namespace && symbol.name ? { symbol } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const interfaceFieldContractFromOption = (value, schemaVersion) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const type = typeRefFromOption(object.type, schemaVersion);
|
||||||
|
const refTargetTypeParam = readString(object, "ref_target_type_param");
|
||||||
|
const edgeCardinality = object.edge_cardinality === undefined
|
||||||
|
? undefined
|
||||||
|
: cardinalityFromOption(object.edge_cardinality);
|
||||||
|
return {
|
||||||
|
required: readBoolean(object, "required") ?? false,
|
||||||
|
readable: readBoolean(object, "readable") ?? false,
|
||||||
|
writable: readBoolean(object, "writable") ?? false,
|
||||||
|
watchable: readBoolean(object, "watchable") ?? false,
|
||||||
|
...(readString(object, "type_param")
|
||||||
|
? { typeParam: readString(object, "type_param") }
|
||||||
|
: {}),
|
||||||
|
...(type ? { type } : {}),
|
||||||
|
...(refTargetTypeParam ? { refTargetTypeParam } : {}),
|
||||||
|
...(edgeCardinality ? { edgeCardinality } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const functionRefFromOption = (value) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
throw new Error("Expected function ref option object");
|
||||||
|
}
|
||||||
|
const packageNamespace = readString(object, "package_namespace");
|
||||||
|
const packageName = readString(object, "package_name");
|
||||||
|
const symbol = readString(object, "symbol");
|
||||||
|
if (!packageNamespace || !packageName || !symbol) {
|
||||||
|
throw new Error("Function ref requires package_namespace, package_name, and symbol");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
packageNamespace,
|
||||||
|
packageName,
|
||||||
|
symbol,
|
||||||
|
...(readString(object, "operation")
|
||||||
|
? { operation: readString(object, "operation") }
|
||||||
|
: {}),
|
||||||
|
...(readString(object, "version_ref")
|
||||||
|
? { versionRef: readString(object, "version_ref") }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const conflictFromOption = (value) => {
|
||||||
|
const normalized = enumName(value);
|
||||||
|
if (!value || normalized === "CONFLICT_STRATEGY_UNSPECIFIED") {
|
||||||
|
return "preserve_conflicts";
|
||||||
|
}
|
||||||
|
if (normalized === "REPLACE") {
|
||||||
|
return "replace";
|
||||||
|
}
|
||||||
|
if (normalized === "PRESERVE_CONFLICTS") {
|
||||||
|
return "preserve_conflicts";
|
||||||
|
}
|
||||||
|
if (normalized === "CRDT") {
|
||||||
|
return "crdt";
|
||||||
|
}
|
||||||
|
throw new Error(`Unknown Camino conflict strategy: ${String(value)}`);
|
||||||
|
};
|
||||||
|
const cardinalityFromOption = (value) => {
|
||||||
|
const normalized = lowerEnumName(value);
|
||||||
|
if (!value || normalized === "cardinality_unspecified") {
|
||||||
|
return "many";
|
||||||
|
}
|
||||||
|
if (normalized === "optional_one" ||
|
||||||
|
normalized === "exactly_one" ||
|
||||||
|
normalized === "many" ||
|
||||||
|
normalized === "many_unique" ||
|
||||||
|
normalized === "many_ordered" ||
|
||||||
|
normalized === "many_unique_ordered") {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
throw new Error(`Unknown Camino cardinality: ${String(value)}`);
|
||||||
|
};
|
||||||
|
const fieldStorageKindFromOption = (value) => {
|
||||||
|
if (value === 5) {
|
||||||
|
return "static_final";
|
||||||
|
}
|
||||||
|
const normalized = lowerEnumName(value);
|
||||||
|
if (!value || normalized === "field_storage_kind_unspecified") {
|
||||||
|
return "stored";
|
||||||
|
}
|
||||||
|
if (normalized === "stored" ||
|
||||||
|
normalized === "derived" ||
|
||||||
|
normalized === "lazy" ||
|
||||||
|
normalized === "external" ||
|
||||||
|
normalized === "static_final") {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
throw new Error(`Unknown Camino field storage kind: ${String(value)}`);
|
||||||
|
};
|
||||||
|
const fieldStorageFromOption = (value) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return {
|
||||||
|
kind: "stored",
|
||||||
|
cache: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const kind = fieldStorageKindFromOption(object.kind);
|
||||||
|
const resolver = object.resolver === undefined
|
||||||
|
? undefined
|
||||||
|
: functionRefFromOption(object.resolver);
|
||||||
|
if ((kind === "derived" || kind === "lazy" || kind === "external") && !resolver) {
|
||||||
|
throw new Error(`${kind} field storage requires resolver`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
kind,
|
||||||
|
...(resolver ? { resolver } : {}),
|
||||||
|
cache: object.cache === true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const fieldOpsFromOption = (value) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const implementation = enumName(object.implementation);
|
||||||
|
if (implementation !== "SERVICE") {
|
||||||
|
throw new Error(`Unsupported Camino field implementation: ${String(object.implementation)}`);
|
||||||
|
}
|
||||||
|
const service = readString(object, "service");
|
||||||
|
if (!service) {
|
||||||
|
throw new Error("SERVICE field_ops requires service");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
implementation: "service",
|
||||||
|
service,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const extractFileDefaults = (sourceFile) => {
|
||||||
|
const text = fs.readFileSync(sourceFile, "utf8");
|
||||||
|
const packageMatch = /^\s*package\s+([a-zA-Z0-9_.]+)\s*;/m.exec(text);
|
||||||
|
const namespaceMatch = /^\s*option\s+\(camino\.schema_namespace\)\s*=\s*"([^"]+)"\s*;/m.exec(text);
|
||||||
|
const versionMatch = /^\s*option\s+\(camino\.schema_version\)\s*=\s*"([^"]+)"\s*;/m.exec(text);
|
||||||
|
return {
|
||||||
|
packageName: packageMatch?.[1] ?? "",
|
||||||
|
schemaNamespace: namespaceMatch?.[1] ?? packageMatch?.[1] ?? "",
|
||||||
|
schemaVersion: versionMatch?.[1] ?? "1",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const protoSymbolFromReflection = (reflection, fallbackVersion) => {
|
||||||
|
if (!reflection?.fullName) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const parts = reflection.fullName.replace(/^\./, "").split(".");
|
||||||
|
const name = parts.pop();
|
||||||
|
if (!name) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
namespace: parts.join("."),
|
||||||
|
name,
|
||||||
|
version: fallbackVersion,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const typeRefForField = (field, schemaVersion) => {
|
||||||
|
const symbol = protoSymbolFromReflection(field.resolvedType, schemaVersion);
|
||||||
|
const enumValues = field.resolvedType instanceof protobuf.Enum
|
||||||
|
? Object.entries(field.resolvedType.values)
|
||||||
|
.filter(([_name, value]) => value !== 0)
|
||||||
|
.map(([name]) => name)
|
||||||
|
: undefined;
|
||||||
|
return {
|
||||||
|
protoType: field.type,
|
||||||
|
...(symbol ? { symbol } : {}),
|
||||||
|
...(enumValues ? { enumValues } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const typeRefForType = (type, fallbackType, schemaVersion) => {
|
||||||
|
const symbol = protoSymbolFromReflection(type ?? null, schemaVersion);
|
||||||
|
return {
|
||||||
|
protoType: type?.fullName?.replace(/^\./, "") ?? fallbackType,
|
||||||
|
...(symbol ? { symbol } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const fieldSchemaFromField = (field, schemaVersion) => {
|
||||||
|
const storage = fieldStorageFromOption(getReflectionOption(field, FIELD_STORAGE_OPTION));
|
||||||
|
const ops = fieldOpsFromOption(getReflectionOption(field, FIELD_OPS_OPTION));
|
||||||
|
if (ops && storage.kind !== "stored" && storage.kind !== "static_final") {
|
||||||
|
throw new Error(`Field ${field.fullName} cannot declare both field_ops and package-resolved storage`);
|
||||||
|
}
|
||||||
|
if (storage.kind === "static_final" && !ops) {
|
||||||
|
throw new Error(`Field ${field.fullName} with static_final storage requires field_ops`);
|
||||||
|
}
|
||||||
|
const interfaceContract = interfaceFieldContractFromOption(getReflectionOption(field, INTERFACE_FIELD_OPTION), schemaVersion);
|
||||||
|
return {
|
||||||
|
name: field.name,
|
||||||
|
tag: field.id,
|
||||||
|
type: typeRefForField(field, schemaVersion),
|
||||||
|
conflict: conflictFromOption(getReflectionOption(field, CONFLICT_OPTION)),
|
||||||
|
repeated: field.repeated,
|
||||||
|
optional: field.optional,
|
||||||
|
storage,
|
||||||
|
...(ops ? { ops } : {}),
|
||||||
|
...(interfaceContract ? { interfaceContract } : {}),
|
||||||
|
isDisplayLabel: getReflectionOption(field, DISPLAY_LABEL_OPTION) === true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const isManyCardinality = (cardinality) => cardinality === "many" ||
|
||||||
|
cardinality === "many_unique" ||
|
||||||
|
cardinality === "many_ordered" ||
|
||||||
|
cardinality === "many_unique_ordered";
|
||||||
|
const materializationFromOption = (value, fallbackVersion) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const classRef = symbolRefFromOption(object.class, {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: fallbackVersion,
|
||||||
|
});
|
||||||
|
if (!classRef.name) {
|
||||||
|
throw new Error("Edge materialization requires class");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
class: classRef,
|
||||||
|
connectProjection: readString(object, "connect_projection") ?? "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const endpointFromOption = (params) => {
|
||||||
|
const object = asObject(params.option);
|
||||||
|
const classRef = asObject(object?.class)
|
||||||
|
? symbolRefFromOption(object?.class, {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: params.fallbackVersion,
|
||||||
|
})
|
||||||
|
: params.fallbackClass;
|
||||||
|
const interfaceRef = asObject(object?.interface)
|
||||||
|
? symbolRefFromOption(object?.interface, {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: params.fallbackVersion,
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
const projection = readString(object, "projection") ?? params.fallbackProjection;
|
||||||
|
const cardinality = object?.cardinality === undefined
|
||||||
|
? params.fallbackCardinality
|
||||||
|
: cardinalityFromOption(object.cardinality);
|
||||||
|
const materialization = materializationFromOption(object?.materialize, params.fallbackVersion);
|
||||||
|
if (!classRef?.name && !interfaceRef?.name) {
|
||||||
|
throw new Error(`Edge endpoint ${projection} requires class or interface`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...(classRef?.name ? { class: classRef } : {}),
|
||||||
|
...(interfaceRef?.name ? { interface: interfaceRef } : {}),
|
||||||
|
projection,
|
||||||
|
cardinality,
|
||||||
|
indexed: readBoolean(object, "indexed") ?? false,
|
||||||
|
...(materialization ? { materialization } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const symbolKey = (ref) => ref ? [ref.namespace, ref.name, ref.version, ref.hash ?? ""].join(":") : "";
|
||||||
|
const endpointRoleKey = (endpoint) => [
|
||||||
|
symbolKey(endpoint.class),
|
||||||
|
symbolKey(endpoint.interface),
|
||||||
|
endpoint.projection,
|
||||||
|
endpoint.cardinality,
|
||||||
|
].join("|");
|
||||||
|
const normalizeEdgeEndpoints = (params) => {
|
||||||
|
if (params.directionality === "directed") {
|
||||||
|
return {
|
||||||
|
fromEndpoint: params.fromEndpoint,
|
||||||
|
toEndpoint: params.toEndpoint,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return endpointRoleKey(params.fromEndpoint) <= endpointRoleKey(params.toEndpoint)
|
||||||
|
? {
|
||||||
|
fromEndpoint: params.fromEndpoint,
|
||||||
|
toEndpoint: params.toEndpoint,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fromEndpoint: params.toEndpoint,
|
||||||
|
toEndpoint: params.fromEndpoint,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const symbolArrayFromOption = (value, defaults) => asArray(value)
|
||||||
|
.map((item) => symbolRefFromOption(item, {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: "",
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
}))
|
||||||
|
.filter((symbol) => Boolean(symbol.name));
|
||||||
|
const isStringType = (type) => type.protoType === "string" || type.protoType === "google.protobuf.StringValue";
|
||||||
|
const validateDisplayLabelFields = (type, fields, operationServices) => {
|
||||||
|
const displayLabelFields = fields.filter((field) => field.isDisplayLabel);
|
||||||
|
if (displayLabelFields.length > 1) {
|
||||||
|
throw new Error(`Class ${type.fullName} declares multiple Camino display label fields`);
|
||||||
|
}
|
||||||
|
const displayLabelField = displayLabelFields[0];
|
||||||
|
if (!displayLabelField) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!displayLabelField.ops) {
|
||||||
|
if (!isStringType(displayLabelField.type)) {
|
||||||
|
throw new Error(`Display label field ${type.fullName}.${displayLabelField.name} must have string type`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const serviceName = displayLabelField.ops.service.replace(/^\./, "");
|
||||||
|
const getOperation = operationServices
|
||||||
|
.find((service) => service.fullName === serviceName)
|
||||||
|
?.operations.find((operation) => operation.name === "Get");
|
||||||
|
if (!getOperation || !isStringType(getOperation.outputType)) {
|
||||||
|
throw new Error(`Display label field ${type.fullName}.${displayLabelField.name} must resolve to string`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const typeBindingFromOption = (value, schemaVersion) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const name = readString(object, "name");
|
||||||
|
const type = typeRefFromOption(object.type, schemaVersion);
|
||||||
|
if (!name || !type) {
|
||||||
|
throw new Error("Interface type_binding requires name and type");
|
||||||
|
}
|
||||||
|
return { name, type };
|
||||||
|
};
|
||||||
|
const interfaceImplementationFromOption = (value, defaults) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
throw new Error("Expected implements option object");
|
||||||
|
}
|
||||||
|
const interfaceId = symbolRefFromOption(object.interface, {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: "",
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
if (!interfaceId.name) {
|
||||||
|
throw new Error("implements option requires interface name");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
interface: interfaceId,
|
||||||
|
typeBindings: asArray(object.type_binding)
|
||||||
|
.map((binding) => typeBindingFromOption(binding, defaults.schemaVersion))
|
||||||
|
.filter((binding) => Boolean(binding)),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const serviceName = (service) => service.fullName.replace(/^\./, "");
|
||||||
|
const operationFromMethod = (method, schemaVersion) => {
|
||||||
|
const fn = functionRefFromOption(getReflectionOption(method, IMPL_OPTION));
|
||||||
|
const methodWithTypes = method;
|
||||||
|
return {
|
||||||
|
name: method.name,
|
||||||
|
inputType: typeRefForType(methodWithTypes.resolvedRequestType, method.requestType, schemaVersion),
|
||||||
|
outputType: typeRefForType(methodWithTypes.resolvedResponseType, method.responseType, schemaVersion),
|
||||||
|
function: fn,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const operationServiceFromService = (service, schemaVersion) => ({
|
||||||
|
name: service.name,
|
||||||
|
fullName: serviceName(service),
|
||||||
|
operations: service.methodsArray.map((method) => operationFromMethod(method, schemaVersion)),
|
||||||
|
});
|
||||||
|
const classSchemaFromType = (type, sourceFile, defaults, operationServices) => {
|
||||||
|
const classOption = asObject(getReflectionOption(type, CLASS_OPTION));
|
||||||
|
if (!classOption) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const fallbackClassRef = {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: type.name,
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
};
|
||||||
|
const classId = symbolRefFromOption(classOption.id, fallbackClassRef);
|
||||||
|
const classVersion = readNumber(classOption, "version") ?? Number(classId.version);
|
||||||
|
if (!Number.isInteger(classVersion) || classVersion <= 0) {
|
||||||
|
throw new Error(`Class ${type.fullName} has invalid Camino class version`);
|
||||||
|
}
|
||||||
|
const fields = type.fieldsArray.map((field) => fieldSchemaFromField(field, defaults.schemaVersion));
|
||||||
|
validateDisplayLabelFields(type, fields, operationServices);
|
||||||
|
const edges = type.fieldsArray.flatMap((field) => {
|
||||||
|
const edgeOption = asObject(getReflectionOption(field, EDGE_OPTION));
|
||||||
|
if (!edgeOption) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const legacyTargetClass = symbolRefFromOption(edgeOption.target, {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: "",
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
const cardinality = cardinalityFromOption(edgeOption.cardinality);
|
||||||
|
const declaredFromEndpoint = endpointFromOption({
|
||||||
|
option: edgeOption.this_endpoint,
|
||||||
|
fallbackClass: classId,
|
||||||
|
fallbackProjection: field.name,
|
||||||
|
fallbackCardinality: cardinality,
|
||||||
|
fallbackVersion: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
const declaredToEndpoint = endpointFromOption({
|
||||||
|
option: edgeOption.other_endpoint,
|
||||||
|
fallbackClass: legacyTargetClass.name ? legacyTargetClass : undefined,
|
||||||
|
fallbackProjection: readString(edgeOption, "inverse") ?? "",
|
||||||
|
fallbackCardinality: "many",
|
||||||
|
fallbackVersion: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
if (!declaredToEndpoint.projection) {
|
||||||
|
throw new Error(`Edge field ${type.fullName}.${field.name} requires other_endpoint.projection or inverse`);
|
||||||
|
}
|
||||||
|
const sourceType = typeRefForField(field, defaults.schemaVersion);
|
||||||
|
if (sourceType.symbol?.namespace !== "camino" ||
|
||||||
|
sourceType.symbol.name !== "Ref") {
|
||||||
|
throw new Error(`Edge field ${type.fullName}.${field.name} must use camino.Ref`);
|
||||||
|
}
|
||||||
|
const isMany = isManyCardinality(declaredFromEndpoint.cardinality);
|
||||||
|
if (isMany && !field.repeated) {
|
||||||
|
throw new Error(`Edge field ${type.fullName}.${field.name} must be repeated for ${declaredFromEndpoint.cardinality}`);
|
||||||
|
}
|
||||||
|
if (!isMany && field.repeated) {
|
||||||
|
throw new Error(`Edge field ${type.fullName}.${field.name} must not be repeated for ${declaredFromEndpoint.cardinality}`);
|
||||||
|
}
|
||||||
|
const directionality = edgeOption.undirected === true ? "undirected" : "directed";
|
||||||
|
const { fromEndpoint, toEndpoint } = normalizeEdgeEndpoints({
|
||||||
|
directionality,
|
||||||
|
fromEndpoint: declaredFromEndpoint,
|
||||||
|
toEndpoint: declaredToEndpoint,
|
||||||
|
});
|
||||||
|
const edgeId = symbolRefFromOption(edgeOption.id, {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: `${type.name}.${field.name}`,
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
const fallbackToClass = toEndpoint.class ?? {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: "",
|
||||||
|
};
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: edgeId,
|
||||||
|
directionality,
|
||||||
|
sourceField: fromEndpoint.projection,
|
||||||
|
fromClass: fromEndpoint.class ?? classId,
|
||||||
|
toClass: fallbackToClass,
|
||||||
|
cardinality: fromEndpoint.cardinality,
|
||||||
|
...(toEndpoint.projection
|
||||||
|
? { inverse: toEndpoint.projection }
|
||||||
|
: {}),
|
||||||
|
fields: [],
|
||||||
|
fromEndpoint,
|
||||||
|
toEndpoint,
|
||||||
|
declaringClass: classId,
|
||||||
|
tags: symbolArrayFromOption(edgeOption.tag, defaults),
|
||||||
|
implements: symbolArrayFromOption(edgeOption.implements, defaults),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const methods = getReflectionOptions(type, METHOD_OPTION)
|
||||||
|
.map(asObject)
|
||||||
|
.filter((option) => Boolean(option))
|
||||||
|
.map((option) => {
|
||||||
|
const name = readString(option, "name");
|
||||||
|
if (!name) {
|
||||||
|
throw new Error(`Method option on ${type.fullName} requires name`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
function: functionRefFromOption(option.function),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const migrations = getReflectionOptions(type, MIGRATION_OPTION)
|
||||||
|
.map(asObject)
|
||||||
|
.filter((option) => Boolean(option))
|
||||||
|
.map((option) => {
|
||||||
|
const fromVersion = readNumber(option, "from_version");
|
||||||
|
const toVersion = readNumber(option, "to_version");
|
||||||
|
if (!fromVersion || !toVersion) {
|
||||||
|
throw new Error(`Migration option on ${type.fullName} requires versions`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
fromVersion,
|
||||||
|
toVersion,
|
||||||
|
function: functionRefFromOption(option.function),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const constructorOption = asObject(getReflectionOption(type, CONSTRUCTOR_OPTION));
|
||||||
|
const constructorSpec = constructorOption
|
||||||
|
? { function: functionRefFromOption(constructorOption.function) }
|
||||||
|
: undefined;
|
||||||
|
return {
|
||||||
|
id: classId,
|
||||||
|
version: classVersion,
|
||||||
|
fields,
|
||||||
|
edges,
|
||||||
|
methods,
|
||||||
|
migrations,
|
||||||
|
operationServices,
|
||||||
|
implements: getReflectionOptions(type, IMPLEMENTS_OPTION).map((option) => interfaceImplementationFromOption(option, defaults)),
|
||||||
|
...(constructorSpec ? { constructorSpec } : {}),
|
||||||
|
sourceFile,
|
||||||
|
protoMessage: type.fullName,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const interfaceSchemaFromType = (type, sourceFile, defaults) => {
|
||||||
|
const interfaceOption = asObject(getReflectionOption(type, INTERFACE_OPTION));
|
||||||
|
if (!interfaceOption) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const classOption = asObject(getReflectionOption(type, CLASS_OPTION));
|
||||||
|
if (classOption) {
|
||||||
|
throw new Error(`${type.fullName} cannot be both a Camino class and interface`);
|
||||||
|
}
|
||||||
|
const fallbackInterfaceRef = {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: type.name,
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
};
|
||||||
|
const interfaceId = symbolRefFromOption(interfaceOption.id, fallbackInterfaceRef);
|
||||||
|
const interfaceVersion = readNumber(interfaceOption, "version") ?? Number(interfaceId.version);
|
||||||
|
if (!Number.isInteger(interfaceVersion) || interfaceVersion <= 0) {
|
||||||
|
throw new Error(`Interface ${type.fullName} has invalid Camino interface version`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: interfaceId,
|
||||||
|
version: interfaceVersion,
|
||||||
|
fields: type.fieldsArray.map((field) => fieldSchemaFromField(field, defaults.schemaVersion)),
|
||||||
|
sourceFile,
|
||||||
|
protoMessage: type.fullName,
|
||||||
|
typeParameters: asArray(interfaceOption.type_parameter)
|
||||||
|
.map((entry) => String(entry).trim())
|
||||||
|
.filter(Boolean),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const walkTypes = (namespace, visit) => {
|
||||||
|
for (const nested of namespace.nestedArray) {
|
||||||
|
if (nested instanceof protobuf.Type) {
|
||||||
|
visit(nested);
|
||||||
|
walkTypes(nested, visit);
|
||||||
|
}
|
||||||
|
else if (nested instanceof protobuf.Namespace) {
|
||||||
|
walkTypes(nested, visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const walkServices = (namespace, visit) => {
|
||||||
|
for (const nested of namespace.nestedArray) {
|
||||||
|
if (nested instanceof protobuf.Service) {
|
||||||
|
visit(nested);
|
||||||
|
}
|
||||||
|
else if (nested instanceof protobuf.Namespace) {
|
||||||
|
walkServices(nested, visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const loadCaminoSchemaRoot = async (sourceFile) => {
|
||||||
|
const absoluteSourceFile = path.resolve(sourceFile);
|
||||||
|
const root = new protobuf.Root();
|
||||||
|
const envIncludeDirs = [
|
||||||
|
process.env.QUIXOS_PROTO_PATH,
|
||||||
|
process.env.CAMINO_PROTO_PATH,
|
||||||
|
]
|
||||||
|
.filter((value) => Boolean(value))
|
||||||
|
.join(path.delimiter)
|
||||||
|
.split(path.delimiter)
|
||||||
|
.map((entry) => entry.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((entry) => path.resolve(entry));
|
||||||
|
const includeDirs = [
|
||||||
|
path.dirname(absoluteSourceFile),
|
||||||
|
path.resolve("../../quixos-protocol/proto"),
|
||||||
|
path.resolve("quixos-protocol/proto"),
|
||||||
|
path.resolve("proto"),
|
||||||
|
...envIncludeDirs,
|
||||||
|
];
|
||||||
|
root.resolvePath = (origin, target) => {
|
||||||
|
if (path.isAbsolute(target) && fs.existsSync(target)) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
const originDir = origin ? path.dirname(origin) : undefined;
|
||||||
|
const candidates = [
|
||||||
|
...(originDir ? [path.resolve(originDir, target)] : []),
|
||||||
|
...includeDirs.map((dir) => path.resolve(dir, target)),
|
||||||
|
];
|
||||||
|
const found = candidates.find((candidate) => fs.existsSync(candidate));
|
||||||
|
return found ?? target;
|
||||||
|
};
|
||||||
|
await root.load(absoluteSourceFile, { keepCase: true });
|
||||||
|
root.resolveAll();
|
||||||
|
return { absoluteSourceFile, root };
|
||||||
|
};
|
||||||
|
export const compileCaminoSchema = async (sourceFile) => {
|
||||||
|
const { absoluteSourceFile, root } = await loadCaminoSchemaRoot(sourceFile);
|
||||||
|
const defaults = extractFileDefaults(absoluteSourceFile);
|
||||||
|
const operationServices = [];
|
||||||
|
walkServices(root, (service) => {
|
||||||
|
operationServices.push(operationServiceFromService(service, defaults.schemaVersion));
|
||||||
|
});
|
||||||
|
const classes = [];
|
||||||
|
const interfaces = [];
|
||||||
|
walkTypes(root, (type) => {
|
||||||
|
const interfaceSchema = interfaceSchemaFromType(type, absoluteSourceFile, defaults);
|
||||||
|
if (interfaceSchema) {
|
||||||
|
interfaces.push(interfaceSchema);
|
||||||
|
}
|
||||||
|
const classSchema = classSchemaFromType(type, absoluteSourceFile, defaults, operationServices);
|
||||||
|
if (classSchema) {
|
||||||
|
classes.push(classSchema);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
sourceFile: absoluteSourceFile,
|
||||||
|
classes,
|
||||||
|
interfaces,
|
||||||
|
};
|
||||||
|
};
|
||||||
Vendored
+138
@@ -0,0 +1,138 @@
|
|||||||
|
export type SymbolRef = {
|
||||||
|
namespace: string;
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
hash?: string;
|
||||||
|
};
|
||||||
|
export type FunctionRef = {
|
||||||
|
packageNamespace: string;
|
||||||
|
packageName: string;
|
||||||
|
symbol: string;
|
||||||
|
operation?: string;
|
||||||
|
versionRef?: string;
|
||||||
|
};
|
||||||
|
export type ConflictStrategy = "replace" | "preserve_conflicts" | "crdt";
|
||||||
|
export type Cardinality = "optional_one" | "exactly_one" | "many" | "many_unique" | "many_ordered" | "many_unique_ordered";
|
||||||
|
export type EdgeDirectionality = "directed" | "undirected";
|
||||||
|
export type EdgeMaterialization = {
|
||||||
|
class: SymbolRef;
|
||||||
|
connectProjection: string;
|
||||||
|
};
|
||||||
|
export type FieldStorageKind = "stored" | "derived" | "lazy" | "external" | "static_final";
|
||||||
|
export type FieldStorage = {
|
||||||
|
kind: FieldStorageKind;
|
||||||
|
resolver?: FunctionRef;
|
||||||
|
cache: boolean;
|
||||||
|
};
|
||||||
|
export type FieldOps = {
|
||||||
|
implementation: "service";
|
||||||
|
service: string;
|
||||||
|
};
|
||||||
|
export type TypeRef = {
|
||||||
|
protoType: string;
|
||||||
|
symbol?: SymbolRef;
|
||||||
|
enumValues?: string[];
|
||||||
|
};
|
||||||
|
export type TypeBinding = {
|
||||||
|
name: string;
|
||||||
|
type: TypeRef;
|
||||||
|
};
|
||||||
|
export type InterfaceFieldContract = {
|
||||||
|
required: boolean;
|
||||||
|
readable: boolean;
|
||||||
|
writable: boolean;
|
||||||
|
watchable: boolean;
|
||||||
|
typeParam?: string;
|
||||||
|
type?: TypeRef;
|
||||||
|
refTargetTypeParam?: string;
|
||||||
|
edgeCardinality?: Cardinality;
|
||||||
|
};
|
||||||
|
export type InterfaceImplementation = {
|
||||||
|
interface: SymbolRef;
|
||||||
|
typeBindings: TypeBinding[];
|
||||||
|
};
|
||||||
|
export type FieldSchema = {
|
||||||
|
name: string;
|
||||||
|
tag: number;
|
||||||
|
type: TypeRef;
|
||||||
|
conflict: ConflictStrategy;
|
||||||
|
repeated: boolean;
|
||||||
|
optional: boolean;
|
||||||
|
storage: FieldStorage;
|
||||||
|
ops?: FieldOps;
|
||||||
|
interfaceContract?: InterfaceFieldContract;
|
||||||
|
isDisplayLabel: boolean;
|
||||||
|
};
|
||||||
|
export type EdgeEndpointSchema = {
|
||||||
|
class?: SymbolRef;
|
||||||
|
interface?: SymbolRef;
|
||||||
|
projection: string;
|
||||||
|
cardinality: Cardinality;
|
||||||
|
indexed: boolean;
|
||||||
|
materialization?: EdgeMaterialization;
|
||||||
|
};
|
||||||
|
export type EdgeSchema = {
|
||||||
|
id: SymbolRef;
|
||||||
|
directionality: EdgeDirectionality;
|
||||||
|
sourceField: string;
|
||||||
|
fromClass: SymbolRef;
|
||||||
|
toClass: SymbolRef;
|
||||||
|
cardinality: Cardinality;
|
||||||
|
inverse?: string;
|
||||||
|
fields: FieldSchema[];
|
||||||
|
fromEndpoint: EdgeEndpointSchema;
|
||||||
|
toEndpoint: EdgeEndpointSchema;
|
||||||
|
declaringClass: SymbolRef;
|
||||||
|
tags: SymbolRef[];
|
||||||
|
implements: SymbolRef[];
|
||||||
|
};
|
||||||
|
export type MethodSchema = {
|
||||||
|
name: string;
|
||||||
|
function: FunctionRef;
|
||||||
|
};
|
||||||
|
export type MigrationSpec = {
|
||||||
|
fromVersion: number;
|
||||||
|
toVersion: number;
|
||||||
|
function: FunctionRef;
|
||||||
|
};
|
||||||
|
export type ConstructorSpec = {
|
||||||
|
function: FunctionRef;
|
||||||
|
};
|
||||||
|
export type OperationSchema = {
|
||||||
|
name: string;
|
||||||
|
inputType: TypeRef;
|
||||||
|
outputType: TypeRef;
|
||||||
|
function: FunctionRef;
|
||||||
|
};
|
||||||
|
export type OperationServiceSchema = {
|
||||||
|
name: string;
|
||||||
|
fullName: string;
|
||||||
|
operations: OperationSchema[];
|
||||||
|
};
|
||||||
|
export type ClassSchema = {
|
||||||
|
id: SymbolRef;
|
||||||
|
version: number;
|
||||||
|
fields: FieldSchema[];
|
||||||
|
edges: EdgeSchema[];
|
||||||
|
methods: MethodSchema[];
|
||||||
|
migrations: MigrationSpec[];
|
||||||
|
operationServices: OperationServiceSchema[];
|
||||||
|
implements: InterfaceImplementation[];
|
||||||
|
constructorSpec?: ConstructorSpec;
|
||||||
|
sourceFile: string;
|
||||||
|
protoMessage: string;
|
||||||
|
};
|
||||||
|
export type InterfaceSchema = {
|
||||||
|
id: SymbolRef;
|
||||||
|
version: number;
|
||||||
|
fields: FieldSchema[];
|
||||||
|
sourceFile: string;
|
||||||
|
protoMessage: string;
|
||||||
|
typeParameters: string[];
|
||||||
|
};
|
||||||
|
export type SchemaCompileResult = {
|
||||||
|
sourceFile: string;
|
||||||
|
classes: ClassSchema[];
|
||||||
|
interfaces: InterfaceSchema[];
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=schema-ir.d.ts.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"schema-ir.d.ts","sourceRoot":"","sources":["../src/schema-ir.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,oBAAoB,GACpB,MAAM,CAAC;AAEX,MAAM,MAAM,WAAW,GACnB,cAAc,GACd,aAAa,GACb,MAAM,GACN,aAAa,GACb,cAAc,GACd,qBAAqB,CAAC;AAE1B,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,YAAY,CAAC;AAE3D,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,SAAS,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GACxB,QAAQ,GACR,SAAS,GACT,MAAM,GACN,UAAU,GACV,cAAc,CAAC;AAEnB,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,cAAc,EAAE,SAAS,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,WAAW,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,SAAS,CAAC;IACd,cAAc,EAAE,kBAAkB,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,SAAS,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,YAAY,EAAE,kBAAkB,CAAC;IACjC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,cAAc,EAAE,SAAS,CAAC;IAC1B,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,WAAW,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,WAAW,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;IAC5C,UAAU,EAAE,uBAAuB,EAAE,CAAC;IACtC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B,CAAC"}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
Generated
+10
-10
@@ -74,17 +74,17 @@
|
|||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1783890039,
|
"lastModified": 1784395768,
|
||||||
"narHash": "sha256-5R7WV6KCSYwQ9SmjmIvRovPjG551icfYa7XHBz68sYA=",
|
"narHash": "sha256-g03xOnzJHiL/edNV6nqURtaPqiapq2uCm7wQTZh4b5s=",
|
||||||
"ref": "refs/heads/exported",
|
"ref": "refs/heads/exported",
|
||||||
"rev": "a287ab31330222d7dcd517c640a5de89f48bc4d0",
|
"rev": "e6b34535afa6e94a118e1f9d082054855e7df632",
|
||||||
"revCount": 4,
|
"revCount": 24,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
|
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"ref": "refs/heads/exported",
|
"ref": "refs/heads/exported",
|
||||||
"rev": "a287ab31330222d7dcd517c640a5de89f48bc4d0",
|
"rev": "e6b34535afa6e94a118e1f9d082054855e7df632",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
|
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
|
||||||
}
|
}
|
||||||
@@ -92,17 +92,17 @@
|
|||||||
"quixosNixHelpers": {
|
"quixosNixHelpers": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1783886666,
|
"lastModified": 1783914102,
|
||||||
"narHash": "sha256-y52TlYwH9RBzzyhpkY/eB21t85BE84idpsg8618MIHg=",
|
"narHash": "sha256-A9EnXxKaODNrSUpw1IlUBR8fe4px5I+wNJFUFGbZY7c=",
|
||||||
"ref": "refs/heads/exported",
|
"ref": "refs/heads/exported",
|
||||||
"rev": "81e55657d9ce0ba092cffd69ee15179ea02aa598",
|
"rev": "1f0c39b01501d646fe97dfc6e5777ccab0bde2f1",
|
||||||
"revCount": 2,
|
"revCount": 6,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
|
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"ref": "refs/heads/exported",
|
"ref": "refs/heads/exported",
|
||||||
"rev": "81e55657d9ce0ba092cffd69ee15179ea02aa598",
|
"rev": "1f0c39b01501d646fe97dfc6e5777ccab0bde2f1",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
|
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
quixos-protocol.url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git?ref=refs/heads/exported&rev=a287ab31330222d7dcd517c640a5de89f48bc4d0";
|
quixos-protocol.url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git?ref=refs/heads/exported&rev=e6b34535afa6e94a118e1f9d082054855e7df632";
|
||||||
quixosNixHelpers = {
|
quixosNixHelpers = {
|
||||||
url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git?ref=refs/heads/exported&rev=81e55657d9ce0ba092cffd69ee15179ea02aa598";
|
url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git?ref=refs/heads/exported&rev=1f0c39b01501d646fe97dfc6e5777ccab0bde2f1";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-3
@@ -4,10 +4,15 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "yarn@4.14.1",
|
"packageManager": "yarn@4.14.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"bin": {
|
||||||
|
"camino-codegen-ts-runtime": "dist/codegen-ts-runtime.js"
|
||||||
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./src/index.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"default": "./src/index.ts"
|
"default": "./dist/index.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -16,9 +21,12 @@
|
|||||||
"typecheck": "yarn generate && tsc --noEmit"
|
"typecheck": "yarn generate && tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@automerge/automerge": "^3.3.0",
|
||||||
"@bufbuild/protobuf": "^2.12.1",
|
"@bufbuild/protobuf": "^2.12.1",
|
||||||
"@connectrpc/connect": "^2.1.2",
|
"@connectrpc/connect": "^2.1.2",
|
||||||
"@connectrpc/connect-node": "^2.1.2"
|
"@connectrpc/connect-node": "^2.1.2",
|
||||||
|
"@quixos/camino-datatypes": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=e061e290709c672d2dc675e5c871733de195211a",
|
||||||
|
"protobufjs": "^7.5.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@bufbuild/protoc-gen-es": "^2.12.1",
|
"@bufbuild/protoc-gen-es": "^2.12.1",
|
||||||
|
|||||||
+235
-25
File diff suppressed because one or more lines are too long
+289
-8
File diff suppressed because one or more lines are too long
@@ -0,0 +1,679 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import protobuf from "protobufjs";
|
||||||
|
import {
|
||||||
|
compileCaminoSchema,
|
||||||
|
loadCaminoSchemaRoot,
|
||||||
|
} from "./schema-compiler.js";
|
||||||
|
import type {
|
||||||
|
ClassSchema,
|
||||||
|
FieldSchema,
|
||||||
|
FunctionRef,
|
||||||
|
InterfaceImplementation,
|
||||||
|
TypeRef,
|
||||||
|
} from "./schema-ir.js";
|
||||||
|
|
||||||
|
const usage = (): never => {
|
||||||
|
console.error(
|
||||||
|
"Usage: camino-codegen-ts-runtime [--react-only] <class-schema.camino.proto> <out-dir> [implementation-import]",
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const required = (value: string | undefined) => value ?? usage();
|
||||||
|
|
||||||
|
const stringLiteral = (value: string) => JSON.stringify(value);
|
||||||
|
|
||||||
|
const upperFirst = (value: string) =>
|
||||||
|
value.length === 0 ? value : `${value[0]?.toUpperCase()}${value.slice(1)}`;
|
||||||
|
|
||||||
|
const lowerFirst = (value: string) =>
|
||||||
|
value.length === 0 ? value : `${value[0]?.toLowerCase()}${value.slice(1)}`;
|
||||||
|
|
||||||
|
const camel = (value: string) =>
|
||||||
|
value
|
||||||
|
.split(/[^a-zA-Z0-9]+/)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((part, index) =>
|
||||||
|
index === 0 ? lowerFirst(part) : upperFirst(lowerFirst(part)),
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
const isIdentifier = (value: string) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
|
||||||
|
|
||||||
|
const classId = (schema: ClassSchema) =>
|
||||||
|
`${schema.id.namespace}:${schema.id.name}:${schema.id.version}:${
|
||||||
|
schema.id.hash ?? ""
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const symbolId = (value: { namespace: string; name: string; version: string; hash?: string }) =>
|
||||||
|
`${value.namespace}:${value.name}:${value.version}:${value.hash ?? ""}`;
|
||||||
|
|
||||||
|
const normalizedProtoType = (value: string) => value.replace(/^\./, "");
|
||||||
|
|
||||||
|
const declarationName = (value: protobuf.Type | protobuf.Enum) => value.name;
|
||||||
|
|
||||||
|
const tsTypeForTypeRef = (
|
||||||
|
type: TypeRef,
|
||||||
|
root?: protobuf.Root,
|
||||||
|
): string => {
|
||||||
|
if (type.symbol) {
|
||||||
|
return `ObjectRef<${stringLiteral(symbolId(type.symbol))}>`;
|
||||||
|
}
|
||||||
|
if (type.enumValues && type.enumValues.length > 0) {
|
||||||
|
return type.enumValues.map(stringLiteral).join(" | ");
|
||||||
|
}
|
||||||
|
const protoType = normalizedProtoType(type.protoType);
|
||||||
|
switch (protoType) {
|
||||||
|
case "string":
|
||||||
|
case "google.protobuf.StringValue":
|
||||||
|
return "string";
|
||||||
|
case "bool":
|
||||||
|
case "google.protobuf.BoolValue":
|
||||||
|
return "boolean";
|
||||||
|
case "double":
|
||||||
|
case "float":
|
||||||
|
case "google.protobuf.DoubleValue":
|
||||||
|
case "google.protobuf.FloatValue":
|
||||||
|
case "int32":
|
||||||
|
case "sint32":
|
||||||
|
case "sfixed32":
|
||||||
|
case "uint32":
|
||||||
|
case "fixed32":
|
||||||
|
case "int64":
|
||||||
|
case "sint64":
|
||||||
|
case "sfixed64":
|
||||||
|
case "uint64":
|
||||||
|
case "fixed64":
|
||||||
|
case "google.protobuf.Int32Value":
|
||||||
|
case "google.protobuf.UInt32Value":
|
||||||
|
case "google.protobuf.Int64Value":
|
||||||
|
case "google.protobuf.UInt64Value":
|
||||||
|
return "number";
|
||||||
|
case "bytes":
|
||||||
|
case "google.protobuf.BytesValue":
|
||||||
|
return "Uint8Array";
|
||||||
|
default:
|
||||||
|
if (protoType === "google.protobuf.Empty") {
|
||||||
|
return "Record<string, never>";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const reflected = root?.lookup(protoType);
|
||||||
|
if (reflected instanceof protobuf.Type || reflected instanceof protobuf.Enum) {
|
||||||
|
return declarationName(reflected);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Unknown imported message types remain opaque at this codegen boundary.
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tsTypeForField = (field: FieldSchema, root?: protobuf.Root): string =>
|
||||||
|
tsTypeForTypeRef(field.type, root);
|
||||||
|
|
||||||
|
const tsValueTypeForField = (field: FieldSchema, root?: protobuf.Root) =>
|
||||||
|
field.repeated ? `${tsTypeForField(field, root)}[]` : tsTypeForField(field, root);
|
||||||
|
|
||||||
|
const tsFieldApiType = (field: FieldSchema, root?: protobuf.Root) =>
|
||||||
|
field.conflict === "crdt"
|
||||||
|
? `CrdtField<${tsValueTypeForField(field, root)}>`
|
||||||
|
: `Field<${tsValueTypeForField(field, root)}>`;
|
||||||
|
|
||||||
|
const functionExportName = (fn: FunctionRef, fallback: string) => {
|
||||||
|
if (isIdentifier(fn.symbol)) {
|
||||||
|
return fn.symbol;
|
||||||
|
}
|
||||||
|
const fallbackName = camel(fallback);
|
||||||
|
if (isIdentifier(fallbackName)) {
|
||||||
|
return fallbackName;
|
||||||
|
}
|
||||||
|
throw new Error(`Cannot generate TS export name for function ${fn.symbol}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const generatedWarning = `// @generated by camino-codegen-ts-runtime. Do not edit.\n`;
|
||||||
|
|
||||||
|
const scalarTsType = (field: protobuf.Field, root: protobuf.Root): string => {
|
||||||
|
const primitive = tsTypeForTypeRef({ protoType: field.type }, root);
|
||||||
|
if (primitive !== "unknown") {
|
||||||
|
return primitive;
|
||||||
|
}
|
||||||
|
const resolved = field.resolvedType;
|
||||||
|
if (resolved instanceof protobuf.Type || resolved instanceof protobuf.Enum) {
|
||||||
|
return declarationName(resolved);
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
};
|
||||||
|
|
||||||
|
const collectProtoDeclarations = (
|
||||||
|
root: protobuf.Root,
|
||||||
|
types: TypeRef[],
|
||||||
|
) => {
|
||||||
|
const declarations = new Map<string, protobuf.Type | protobuf.Enum>();
|
||||||
|
const visit = (value: protobuf.Type | protobuf.Enum) => {
|
||||||
|
const key = value.fullName;
|
||||||
|
if (declarations.has(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
declarations.set(key, value);
|
||||||
|
if (value instanceof protobuf.Type) {
|
||||||
|
for (const field of value.fieldsArray) {
|
||||||
|
if (
|
||||||
|
field.resolvedType instanceof protobuf.Type ||
|
||||||
|
field.resolvedType instanceof protobuf.Enum
|
||||||
|
) {
|
||||||
|
visit(field.resolvedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const type of types) {
|
||||||
|
if (type.symbol || !type.protoType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const reflected = root.lookup(normalizedProtoType(type.protoType));
|
||||||
|
if (reflected instanceof protobuf.Type || reflected instanceof protobuf.Enum) {
|
||||||
|
visit(reflected);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Opaque imported types are represented as unknown by the generated API.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...declarations.values()]
|
||||||
|
.map((value) => {
|
||||||
|
if (value instanceof protobuf.Enum) {
|
||||||
|
return `export type ${declarationName(value)} = ${Object.keys(value.values)
|
||||||
|
.map(stringLiteral)
|
||||||
|
.join(" | ")};`;
|
||||||
|
}
|
||||||
|
const fields = value.fieldsArray.map((field) => {
|
||||||
|
const fieldType = scalarTsType(field, root);
|
||||||
|
const valueType = field.map
|
||||||
|
? `Record<string, ${fieldType}>`
|
||||||
|
: field.repeated
|
||||||
|
? `${fieldType}[]`
|
||||||
|
: fieldType;
|
||||||
|
return ` ${JSON.stringify(field.name)}${field.optional ? "?" : ""}: ${valueType};`;
|
||||||
|
});
|
||||||
|
return `export type ${declarationName(value)} = {\n${fields.join("\n")}\n};`;
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
type FunctionSpec = {
|
||||||
|
exportName: string;
|
||||||
|
symbol: string;
|
||||||
|
operation?: string;
|
||||||
|
packageNamespace: string;
|
||||||
|
packageName: string;
|
||||||
|
field?: {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
storage: string;
|
||||||
|
conflict: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const fieldTypeId = (field: FieldSchema) =>
|
||||||
|
field.type.symbol ? symbolId(field.type.symbol) : field.type.protoType;
|
||||||
|
|
||||||
|
const crdtStorageTypeId = (field: FieldSchema) =>
|
||||||
|
field.type.protoType ||
|
||||||
|
(field.type.symbol
|
||||||
|
? `${field.type.symbol.namespace}.${field.type.symbol.name}`
|
||||||
|
: fieldTypeId(field));
|
||||||
|
|
||||||
|
const fieldStorageId = (field: FieldSchema) =>
|
||||||
|
field.ops && field.storage.kind === "stored" ? "derived" : field.storage.kind;
|
||||||
|
|
||||||
|
const interfaceTypeName = (implementation: InterfaceImplementation) =>
|
||||||
|
`${implementation.interface.name}Ref`;
|
||||||
|
|
||||||
|
const renderInterfaceRefTypes = (schema: ClassSchema) =>
|
||||||
|
schema.implements
|
||||||
|
.map((implementation) => {
|
||||||
|
const typeParams = implementation.typeBindings.map((binding) => binding.name);
|
||||||
|
const generic =
|
||||||
|
typeParams.length > 0 ? `<${typeParams.join(", ")}>` : "";
|
||||||
|
const bindings =
|
||||||
|
typeParams.length > 0
|
||||||
|
? typeParams
|
||||||
|
.map((param) => ` readonly ${param}: ${param};`)
|
||||||
|
.join("\n")
|
||||||
|
: " readonly value: unknown;";
|
||||||
|
return `export type ${interfaceTypeName(implementation)}${generic} = ObjectRef<string> & {
|
||||||
|
readonly $caminoInterfaces: {
|
||||||
|
readonly ${stringLiteral(symbolId(implementation.interface))}: {
|
||||||
|
${bindings}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};`;
|
||||||
|
})
|
||||||
|
.join("\n\n");
|
||||||
|
|
||||||
|
const interfaceMixins = (schema: ClassSchema, root: protobuf.Root) =>
|
||||||
|
schema.implements.map((implementation) => {
|
||||||
|
const typeArgs = implementation.typeBindings.map((binding) =>
|
||||||
|
tsTypeForTypeRef(binding.type, root),
|
||||||
|
);
|
||||||
|
return `${interfaceTypeName(implementation)}${
|
||||||
|
typeArgs.length > 0 ? `<${typeArgs.join(", ")}>` : ""
|
||||||
|
}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const operationFunctionSpecs = (schema: ClassSchema): FunctionSpec[] =>
|
||||||
|
schema.fields.flatMap((field) => {
|
||||||
|
const serviceName = field.ops?.service.replace(/^\./, "");
|
||||||
|
if (!serviceName) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const service = schema.operationServices.find(
|
||||||
|
(candidate) => candidate.fullName === serviceName,
|
||||||
|
);
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(
|
||||||
|
`${schema.id.name}.${field.name} points to missing operation service ${field.ops?.service}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return service.operations.map((operation) => ({
|
||||||
|
exportName: functionExportName(
|
||||||
|
operation.function,
|
||||||
|
`${field.name}_${operation.name}`,
|
||||||
|
),
|
||||||
|
symbol: operation.function.symbol,
|
||||||
|
...(operation.function.operation
|
||||||
|
? { operation: operation.function.operation }
|
||||||
|
: {}),
|
||||||
|
packageNamespace: operation.function.packageNamespace,
|
||||||
|
packageName: operation.function.packageName,
|
||||||
|
field: {
|
||||||
|
name: field.name,
|
||||||
|
type: fieldTypeId(field),
|
||||||
|
storage: fieldStorageId(field),
|
||||||
|
conflict: field.conflict,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderRuntime = (schema: ClassSchema, root: protobuf.Root) => {
|
||||||
|
const className = schema.id.name;
|
||||||
|
const objectTypeName = `${className}Object`;
|
||||||
|
const refTypeName = `${className}Ref`;
|
||||||
|
const contextTypeName = `${className}Context`;
|
||||||
|
const implementationTypeName = `${className}Implementation`;
|
||||||
|
const serveName = `serve${className}Runtime`;
|
||||||
|
const createObjectName = `create${className}Object`;
|
||||||
|
const interfaceRefTypes = renderInterfaceRefTypes(schema);
|
||||||
|
const refType =
|
||||||
|
[ `ObjectRef<${stringLiteral(classId(schema))}>`, ...interfaceMixins(schema, root)]
|
||||||
|
.join(" & ");
|
||||||
|
const edgeFields = new Set(schema.edges.map((edge) => edge.sourceField));
|
||||||
|
const writableFields = schema.fields.filter(
|
||||||
|
(field) =>
|
||||||
|
field.storage.kind === "stored" &&
|
||||||
|
!field.ops &&
|
||||||
|
!edgeFields.has(field.name),
|
||||||
|
);
|
||||||
|
const functionSpecs = [
|
||||||
|
...(schema.constructorSpec
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
exportName: functionExportName(
|
||||||
|
schema.constructorSpec.function,
|
||||||
|
"constructor",
|
||||||
|
),
|
||||||
|
symbol: schema.constructorSpec.function.symbol,
|
||||||
|
...(schema.constructorSpec.function.operation
|
||||||
|
? { operation: schema.constructorSpec.function.operation }
|
||||||
|
: {}),
|
||||||
|
packageNamespace: schema.constructorSpec.function.packageNamespace,
|
||||||
|
packageName: schema.constructorSpec.function.packageName,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...schema.methods.map((method) => ({
|
||||||
|
exportName: functionExportName(method.function, method.name),
|
||||||
|
symbol: method.function.symbol,
|
||||||
|
...(method.function.operation ? { operation: method.function.operation } : {}),
|
||||||
|
packageNamespace: method.function.packageNamespace,
|
||||||
|
packageName: method.function.packageName,
|
||||||
|
})),
|
||||||
|
...schema.fields.flatMap((field) =>
|
||||||
|
field.storage.resolver
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
exportName: functionExportName(field.storage.resolver, field.name),
|
||||||
|
symbol: field.storage.resolver.symbol,
|
||||||
|
...(field.storage.resolver.operation
|
||||||
|
? { operation: field.storage.resolver.operation }
|
||||||
|
: {}),
|
||||||
|
packageNamespace: field.storage.resolver.packageNamespace,
|
||||||
|
packageName: field.storage.resolver.packageName,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
...operationFunctionSpecs(schema),
|
||||||
|
];
|
||||||
|
const firstFunction = functionSpecs[0];
|
||||||
|
if (!firstFunction) {
|
||||||
|
throw new Error(`${schema.id.name} has no runtime functions to generate`);
|
||||||
|
}
|
||||||
|
const mixedPackage = functionSpecs.find(
|
||||||
|
(spec) =>
|
||||||
|
spec.packageNamespace !== firstFunction.packageNamespace ||
|
||||||
|
spec.packageName !== firstFunction.packageName,
|
||||||
|
);
|
||||||
|
if (mixedPackage) {
|
||||||
|
throw new Error(
|
||||||
|
`${schema.id.name} points to multiple packages; v0 TS runtime generation expects one package`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${generatedWarning}import {
|
||||||
|
type CaminoClient,
|
||||||
|
createCrdtField,
|
||||||
|
createField,
|
||||||
|
type CrdtField,
|
||||||
|
type Field,
|
||||||
|
type FieldOperation,
|
||||||
|
type InvokeRequest,
|
||||||
|
objectRef,
|
||||||
|
type ObjectRef,
|
||||||
|
type RuntimeHandler,
|
||||||
|
serveQuixosPackageRuntime,
|
||||||
|
} from "@quixos/camino-package-runtime";
|
||||||
|
|
||||||
|
${collectProtoDeclarations(root, schema.implements.flatMap((entry) => entry.typeBindings.map((binding) => binding.type)))}
|
||||||
|
|
||||||
|
${interfaceRefTypes ? `${interfaceRefTypes}\n\n` : ""}export type ${refTypeName} = ${refType};
|
||||||
|
|
||||||
|
export type ${objectTypeName} = {
|
||||||
|
id: ${refTypeName};
|
||||||
|
${writableFields
|
||||||
|
.map(
|
||||||
|
(field) =>
|
||||||
|
` ${camel(field.name)}: ${tsFieldApiType(field, root)};`,
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ${contextTypeName} = {
|
||||||
|
object: ${objectTypeName};
|
||||||
|
camino: CaminoClient;
|
||||||
|
request: InvokeRequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ${implementationTypeName} = {
|
||||||
|
${functionSpecs
|
||||||
|
.map((spec) =>
|
||||||
|
spec.operation
|
||||||
|
? ` ${spec.exportName}: FieldOperation<${contextTypeName}>;`
|
||||||
|
: ` ${spec.exportName}: RuntimeHandler<${contextTypeName}>;`,
|
||||||
|
)
|
||||||
|
.filter((line, index, lines) => lines.indexOf(line) === index)
|
||||||
|
.join("\n")}
|
||||||
|
};
|
||||||
|
|
||||||
|
const packageNamespace = ${stringLiteral(firstFunction.packageNamespace)};
|
||||||
|
const packageName = ${stringLiteral(firstFunction.packageName)};
|
||||||
|
const runtimeProtocolVersion = ${stringLiteral("camino-orch-v0")};
|
||||||
|
|
||||||
|
const functions = [
|
||||||
|
${functionSpecs
|
||||||
|
.map(
|
||||||
|
(spec) =>
|
||||||
|
` { exportName: ${stringLiteral(spec.exportName)}, symbol: ${stringLiteral(spec.symbol)}${
|
||||||
|
spec.operation ? `, operation: ${stringLiteral(spec.operation)}` : ""
|
||||||
|
}${
|
||||||
|
spec.field
|
||||||
|
? `, field: { name: ${stringLiteral(spec.field.name)}, type: ${stringLiteral(spec.field.type)}, storage: ${stringLiteral(spec.field.storage)}, conflict: ${stringLiteral(spec.field.conflict)} }`
|
||||||
|
: ""
|
||||||
|
} },`,
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const ${createObjectName} = (
|
||||||
|
camino: CaminoClient,
|
||||||
|
objectId: string,
|
||||||
|
): ${objectTypeName} => {
|
||||||
|
const id = objectRef<${stringLiteral(classId(schema))}>(objectId) as ${refTypeName};
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
${writableFields
|
||||||
|
.map(
|
||||||
|
(fieldSchema) =>
|
||||||
|
fieldSchema.conflict === "crdt"
|
||||||
|
? ` ${camel(fieldSchema.name)}: createCrdtField(camino, id, ${stringLiteral(fieldSchema.name)}, ${stringLiteral(crdtStorageTypeId(fieldSchema))}),`
|
||||||
|
: ` ${camel(fieldSchema.name)}: createField(camino, id, ${stringLiteral(fieldSchema.name)}),`,
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const create${className}Context = (
|
||||||
|
camino: CaminoClient,
|
||||||
|
request: InvokeRequest,
|
||||||
|
): ${contextTypeName} => ({
|
||||||
|
object: ${createObjectName}(camino, request.objectId),
|
||||||
|
camino,
|
||||||
|
request,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ${serveName} = (implementation: ${implementationTypeName}) => {
|
||||||
|
serveQuixosPackageRuntime({
|
||||||
|
packageNamespace,
|
||||||
|
packageName,
|
||||||
|
runtimeProtocolVersion,
|
||||||
|
functions,
|
||||||
|
implementation,
|
||||||
|
createContext: create${className}Context,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const implementationNamed = (schema: ClassSchema, name: string) =>
|
||||||
|
schema.implements.find(
|
||||||
|
(entry) =>
|
||||||
|
entry.interface.namespace === "quixos.react" &&
|
||||||
|
entry.interface.name === name,
|
||||||
|
);
|
||||||
|
|
||||||
|
const requiredBinding = (
|
||||||
|
implementation: InterfaceImplementation,
|
||||||
|
name: string,
|
||||||
|
) => {
|
||||||
|
const binding = implementation.typeBindings.find((entry) => entry.name === name);
|
||||||
|
if (!binding) {
|
||||||
|
throw new Error(
|
||||||
|
`${implementation.interface.name} implementation is missing ${name}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return binding.type;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderReactClient = (schema: ClassSchema, root: protobuf.Root) => {
|
||||||
|
const react = implementationNamed(schema, "ReactComponent");
|
||||||
|
const componentFor = implementationNamed(schema, "ReactComponentFor");
|
||||||
|
if (!react || !componentFor) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const renderProps = requiredBinding(react, "RenderProps");
|
||||||
|
const action = requiredBinding(react, "Action");
|
||||||
|
const object = requiredBinding(componentFor, "Object");
|
||||||
|
if (!object.symbol) {
|
||||||
|
throw new Error(`${schema.id.name}.ReactComponentFor.Object must bind a class symbol`);
|
||||||
|
}
|
||||||
|
const edge = schema.edges.find((candidate) => candidate.sourceField === "for_object");
|
||||||
|
if (!edge) {
|
||||||
|
throw new Error(`${schema.id.name} must declare its ReactComponentFor edge`);
|
||||||
|
}
|
||||||
|
const componentEndpoint =
|
||||||
|
edge.fromEndpoint.projection === "for_object"
|
||||||
|
? edge.fromEndpoint
|
||||||
|
: edge.toEndpoint.projection === "for_object"
|
||||||
|
? edge.toEndpoint
|
||||||
|
: undefined;
|
||||||
|
const objectEndpoint =
|
||||||
|
componentEndpoint === edge.fromEndpoint ? edge.toEndpoint : edge.fromEndpoint;
|
||||||
|
if (!componentEndpoint || !objectEndpoint.projection) {
|
||||||
|
throw new Error(`${schema.id.name}.for_object edge has no inverse projection`);
|
||||||
|
}
|
||||||
|
if (componentEndpoint.cardinality !== "exactly_one") {
|
||||||
|
throw new Error(`${schema.id.name}.for_object must have exactly_one cardinality`);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!objectEndpoint.materialization ||
|
||||||
|
symbolId(objectEndpoint.materialization.class) !== symbolId(schema.id)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`${schema.id.name}.${objectEndpoint.projection} must materialize ${classId(schema)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const className = schema.id.name;
|
||||||
|
const componentName = className.endsWith("ReactComponent")
|
||||||
|
? `${className.slice(0, -"ReactComponent".length)}Component`
|
||||||
|
: className;
|
||||||
|
const propsTypeName = `${componentName}Props`;
|
||||||
|
const renderPropsType = tsTypeForTypeRef(renderProps, root);
|
||||||
|
const actionType = tsTypeForTypeRef(action, root);
|
||||||
|
const objectType = tsTypeForTypeRef(object, root);
|
||||||
|
const declarations = collectProtoDeclarations(root, [renderProps, action]);
|
||||||
|
|
||||||
|
return `${generatedWarning}import {
|
||||||
|
createReactComponentFor,
|
||||||
|
type ReactComponentHostProps,
|
||||||
|
type ObjectRef,
|
||||||
|
} from "@quixos/camino-react-runtime";
|
||||||
|
|
||||||
|
${declarations}
|
||||||
|
|
||||||
|
export type ${propsTypeName} = {
|
||||||
|
forObject: ${objectType};
|
||||||
|
} & ${renderPropsType} & ReactComponentHostProps<${actionType}>;
|
||||||
|
|
||||||
|
export const ${componentName} = createReactComponentFor<
|
||||||
|
${objectType},
|
||||||
|
${renderPropsType},
|
||||||
|
${actionType}
|
||||||
|
>({
|
||||||
|
componentClassId: ${stringLiteral(classId(schema))},
|
||||||
|
projection: ${stringLiteral(objectEndpoint.projection)},
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reactRuntimeDeclaration = `${generatedWarning}declare module "@quixos/camino-react-runtime" {
|
||||||
|
import type * as AutomergeNamespace from "@automerge/automerge";
|
||||||
|
|
||||||
|
export type ObjectRef<ClassId extends string> = string & {
|
||||||
|
readonly $caminoClass: ClassId;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LiveFieldProp<T> = {
|
||||||
|
value: T;
|
||||||
|
source: {
|
||||||
|
objectId: string;
|
||||||
|
fieldName: string;
|
||||||
|
fieldType?: string;
|
||||||
|
fieldStorage?: string;
|
||||||
|
conflictStrategy?: string;
|
||||||
|
revision?: string | number | bigint;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReactComponentHostProps<Action> = {
|
||||||
|
onAction?: (action: Action) => void;
|
||||||
|
fallback?: unknown;
|
||||||
|
className?: string;
|
||||||
|
style?: Record<string, string | number | undefined>;
|
||||||
|
onError?: (error: Error) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReactComponentImplementationProps<CaminoProps, RenderProps, Action> = {
|
||||||
|
camino: CaminoProps;
|
||||||
|
render: RenderProps;
|
||||||
|
dispatch: (action: Action) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createReactComponentFor: <
|
||||||
|
ForObject extends ObjectRef<string>,
|
||||||
|
RenderProps extends object,
|
||||||
|
Action,
|
||||||
|
>(config: {
|
||||||
|
componentClassId: string;
|
||||||
|
projection: string;
|
||||||
|
}) => (props: {
|
||||||
|
forObject: ForObject;
|
||||||
|
} & RenderProps & ReactComponentHostProps<Action>) => any;
|
||||||
|
|
||||||
|
export const callObjectMethod: <Result = unknown>(
|
||||||
|
object: ObjectRef<string>,
|
||||||
|
methodName: string,
|
||||||
|
input?: Record<string, unknown>,
|
||||||
|
) => Promise<Result>;
|
||||||
|
|
||||||
|
export const h: (...args: any[]) => any;
|
||||||
|
export const useLiveField: <T>(
|
||||||
|
field: LiveFieldProp<T>,
|
||||||
|
) => [T, (value: T) => void | Promise<void>];
|
||||||
|
export const Automerge: typeof AutomergeNamespace;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const renderServer = (schema: ClassSchema, implementationImport: string) => {
|
||||||
|
const className = schema.id.name;
|
||||||
|
return `${generatedWarning}import * as implementation from ${stringLiteral(implementationImport)};
|
||||||
|
import { serve${className}Runtime } from "./${schema.id.namespace}.${className}.runtime.js";
|
||||||
|
|
||||||
|
serve${className}Runtime(implementation);
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
const reactOnly = process.argv[2] === "--react-only";
|
||||||
|
const offset = reactOnly ? 1 : 0;
|
||||||
|
const sourceFile = required(process.argv[2 + offset]);
|
||||||
|
const outDir = required(process.argv[3 + offset]);
|
||||||
|
const implementationImport = process.argv[4 + offset] ?? "../task.impl.js";
|
||||||
|
const compiled = await compileCaminoSchema(sourceFile);
|
||||||
|
const { root } = await loadCaminoSchemaRoot(sourceFile);
|
||||||
|
if (compiled.classes.length !== 1) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected exactly one Camino class in ${sourceFile}, found ${compiled.classes.length}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const schema = compiled.classes[0];
|
||||||
|
if (!schema) {
|
||||||
|
throw new Error(`No Camino class found in ${sourceFile}`);
|
||||||
|
}
|
||||||
|
fs.mkdirSync(outDir, { recursive: true });
|
||||||
|
const baseName = `${schema.id.namespace}.${schema.id.name}`;
|
||||||
|
const reactClient = renderReactClient(schema, root);
|
||||||
|
if (!reactOnly) {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(outDir, `${baseName}.runtime.ts`),
|
||||||
|
renderRuntime(schema, root),
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(outDir, `${baseName}.server.ts`),
|
||||||
|
renderServer(schema, implementationImport),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (reactClient) {
|
||||||
|
fs.writeFileSync(path.join(outDir, `${baseName}.react.ts`), reactClient);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(outDir, "camino-react-runtime.d.ts"),
|
||||||
|
reactRuntimeDeclaration,
|
||||||
|
);
|
||||||
|
} else if (reactOnly) {
|
||||||
|
throw new Error(`${schema.id.name} does not implement ReactComponentFor`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await main();
|
||||||
+215
-11
@@ -8,6 +8,13 @@ import {
|
|||||||
type HandlerContext,
|
type HandlerContext,
|
||||||
type Interceptor,
|
type Interceptor,
|
||||||
} from "@connectrpc/connect";
|
} from "@connectrpc/connect";
|
||||||
|
import * as Automerge from "@automerge/automerge";
|
||||||
|
import {
|
||||||
|
createAutomergeFieldValue,
|
||||||
|
encodeAutomergeFieldWrite,
|
||||||
|
loadAutomergeFieldDoc,
|
||||||
|
type CaminoAutomergeDoc,
|
||||||
|
} from "@quixos/camino-datatypes/crdt-automerge";
|
||||||
import {
|
import {
|
||||||
connectNodeAdapter,
|
connectNodeAdapter,
|
||||||
createConnectTransport,
|
createConnectTransport,
|
||||||
@@ -15,11 +22,13 @@ import {
|
|||||||
import {
|
import {
|
||||||
CaminoService,
|
CaminoService,
|
||||||
CrdtValueSchema,
|
CrdtValueSchema,
|
||||||
|
FieldValueSourceSchema,
|
||||||
ListValueSchema,
|
ListValueSchema,
|
||||||
NullValueSchema,
|
NullValueSchema,
|
||||||
ObjectValueSchema,
|
ObjectValueSchema,
|
||||||
RefValueSchema,
|
RefValueSchema,
|
||||||
ValueSchema,
|
ValueSchema,
|
||||||
|
ValueSourceSchema,
|
||||||
type Value,
|
type Value,
|
||||||
} from "./camino/api_pb.js";
|
} from "./camino/api_pb.js";
|
||||||
import {
|
import {
|
||||||
@@ -181,6 +190,49 @@ export type Field<T> = {
|
|||||||
set(value: T): Promise<void>;
|
set(value: T): Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CrdtField<T> = Field<CaminoAutomergeDoc<T>> & {
|
||||||
|
change(fn: (doc: { value: T }) => void): Promise<CaminoAutomergeDoc<T>>;
|
||||||
|
replaceFromJson(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> = (
|
export type RuntimeHandler<Context> = (
|
||||||
context: Context,
|
context: Context,
|
||||||
) => Promise<unknown> | unknown;
|
) => Promise<unknown> | unknown;
|
||||||
@@ -374,6 +426,12 @@ export type RuntimeFunctionSpec<ExportName extends string = string> = {
|
|||||||
exportName: ExportName;
|
exportName: ExportName;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
operation?: string;
|
operation?: string;
|
||||||
|
field?: {
|
||||||
|
name: string;
|
||||||
|
type?: string;
|
||||||
|
storage?: string;
|
||||||
|
conflict?: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncEventQueue<T> implements AsyncIterableIterator<T> {
|
class AsyncEventQueue<T> implements AsyncIterableIterator<T> {
|
||||||
@@ -429,7 +487,67 @@ const bytesToBase64 = (value: Uint8Array) =>
|
|||||||
|
|
||||||
const base64ToBytes = (value: string) => Buffer.from(value, "base64");
|
const base64ToBytes = (value: string) => Buffer.from(value, "base64");
|
||||||
|
|
||||||
export const jsToProtoValue = (value: unknown): Value => {
|
type FieldCodec = {
|
||||||
|
fieldType?: string;
|
||||||
|
conflictStrategy?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 codecFromSource = (value: unknown): FieldCodec | undefined => {
|
||||||
|
if (!isRecord(value) || !isRecord(value.field)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const field = value.field;
|
||||||
|
return {
|
||||||
|
...(typeof field.fieldType === "string" ? { fieldType: field.fieldType } : {}),
|
||||||
|
...(typeof field.conflictStrategy === "string"
|
||||||
|
? { conflictStrategy: field.conflictStrategy }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const withSource = (value: Value, source: unknown): Value => {
|
||||||
|
const sourceValue = sourceToProto(source);
|
||||||
|
return sourceValue ? create(ValueSchema, { ...value, source: sourceValue }) : value;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const jsToProtoValue = (value: unknown, codec?: FieldCodec): Value => {
|
||||||
|
if (codec?.conflictStrategy === "crdt" && codec.fieldType) {
|
||||||
|
return jsToProtoValue(
|
||||||
|
encodeAutomergeFieldWrite(value, codec.fieldType),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
return create(ValueSchema, {
|
return create(ValueSchema, {
|
||||||
kind: {
|
kind: {
|
||||||
@@ -468,12 +586,18 @@ export const jsToProtoValue = (value: unknown): Value => {
|
|||||||
kind: {
|
kind: {
|
||||||
case: "listValue",
|
case: "listValue",
|
||||||
value: create(ListValueSchema, {
|
value: create(ListValueSchema, {
|
||||||
values: value.map(jsToProtoValue),
|
values: value.map((entry) => jsToProtoValue(entry)),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isRecord(value)) {
|
if (isRecord(value)) {
|
||||||
|
if (Object.hasOwn(value, "$caminoValue") && isRecord(value.$caminoSource)) {
|
||||||
|
return withSource(
|
||||||
|
jsToProtoValue(value.$caminoValue, codecFromSource(value.$caminoSource)),
|
||||||
|
value.$caminoSource,
|
||||||
|
);
|
||||||
|
}
|
||||||
if (typeof value.$caminoRef === "string") {
|
if (typeof value.$caminoRef === "string") {
|
||||||
return create(ValueSchema, {
|
return create(ValueSchema, {
|
||||||
kind: {
|
kind: {
|
||||||
@@ -545,11 +669,12 @@ export const protoValueToJs = (value: Value | undefined): unknown => {
|
|||||||
case "refValue":
|
case "refValue":
|
||||||
return value.kind.value.objectId;
|
return value.kind.value.objectId;
|
||||||
case "crdtValue":
|
case "crdtValue":
|
||||||
return {
|
return loadAutomergeFieldDoc({
|
||||||
$caminoCrdtType: value.kind.value.type,
|
$caminoCrdtType: value.kind.value.type,
|
||||||
$caminoCrdtEncoding: value.kind.value.encoding,
|
$caminoCrdtEncoding:
|
||||||
|
value.kind.value.encoding === "base64" ? "base64" : "base64",
|
||||||
$caminoCrdtPayload: bytesToBase64(value.kind.value.payload),
|
$caminoCrdtPayload: bytesToBase64(value.kind.value.payload),
|
||||||
};
|
}, value.kind.value.type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -618,10 +743,43 @@ const derivedDependencyToProto = (dependency: DerivedDependency) =>
|
|||||||
sourceField: dependency.kind === "edges" ? dependency.sourceField ?? "" : "",
|
sourceField: dependency.kind === "edges" ? dependency.sourceField ?? "" : "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const derivedEmissionToProto = (event: DerivedWatchEmission): WatchEvent =>
|
const fieldSourceForSpec = (
|
||||||
|
spec: RuntimeFunctionSpec,
|
||||||
|
objectId: string,
|
||||||
|
): LiveFieldValue<unknown>["$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, {
|
create(WatchEventSchema, {
|
||||||
watchId: event.watchId,
|
watchId: event.watchId,
|
||||||
value: jsToProtoValue(event.value ?? null),
|
value: jsToProtoValue(
|
||||||
|
event.error ? null : wrapFieldResult(spec, objectId, event.value ?? null),
|
||||||
|
),
|
||||||
dependencies: event.dependencies.map(derivedDependencyToProto),
|
dependencies: event.dependencies.map(derivedDependencyToProto),
|
||||||
error: event.error ?? "",
|
error: event.error ?? "",
|
||||||
initial: event.initial,
|
initial: event.initial,
|
||||||
@@ -642,6 +800,7 @@ export const createField = <T>(
|
|||||||
camino: CaminoClient,
|
camino: CaminoClient,
|
||||||
objectId: string,
|
objectId: string,
|
||||||
fieldName: string,
|
fieldName: string,
|
||||||
|
codec?: FieldCodec,
|
||||||
): Field<T> => ({
|
): Field<T> => ({
|
||||||
async get() {
|
async get() {
|
||||||
recordDerivedDependency({ kind: "field", objectId, fieldName });
|
recordDerivedDependency({ kind: "field", objectId, fieldName });
|
||||||
@@ -652,11 +811,44 @@ export const createField = <T>(
|
|||||||
await camino.setField({
|
await camino.setField({
|
||||||
objectId,
|
objectId,
|
||||||
fieldName,
|
fieldName,
|
||||||
value: jsToProtoValue(value),
|
value: jsToProtoValue(value, codec),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const createCrdtField = <T>(
|
||||||
|
camino: CaminoClient,
|
||||||
|
objectId: string,
|
||||||
|
fieldName: string,
|
||||||
|
fieldType: string,
|
||||||
|
): CrdtField<T> => {
|
||||||
|
const field = createField<CaminoAutomergeDoc<T>>(camino, objectId, fieldName, {
|
||||||
|
fieldType,
|
||||||
|
conflictStrategy: "crdt",
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
async change(fn) {
|
||||||
|
const current = await field.get();
|
||||||
|
if (!current) {
|
||||||
|
throw new Error(`Cannot change missing CRDT field ${fieldName}`);
|
||||||
|
}
|
||||||
|
const next = Automerge.change(current, (doc) => {
|
||||||
|
fn(doc as { value: T });
|
||||||
|
}) as CaminoAutomergeDoc<T>;
|
||||||
|
await field.set(next);
|
||||||
|
return next;
|
||||||
|
},
|
||||||
|
async replaceFromJson(value) {
|
||||||
|
await camino.setField({
|
||||||
|
objectId,
|
||||||
|
fieldName,
|
||||||
|
value: jsToProtoValue(createAutomergeFieldValue(value, fieldType)),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const createDependencyTrackingCaminoClient = (
|
const createDependencyTrackingCaminoClient = (
|
||||||
camino: CaminoClient,
|
camino: CaminoClient,
|
||||||
): CaminoClient =>
|
): CaminoClient =>
|
||||||
@@ -721,6 +913,11 @@ export const serveQuixosPackageRuntime = <
|
|||||||
const port = Number(process.env.QUIXOS_RUNTIME_PORT ?? "0");
|
const port = Number(process.env.QUIXOS_RUNTIME_PORT ?? "0");
|
||||||
const caminoUrl = process.env.CAMINO_URL ?? "http://127.0.0.1:7310";
|
const caminoUrl = process.env.CAMINO_URL ?? "http://127.0.0.1:7310";
|
||||||
const runtimeToken = process.env.CAMINO_RUNTIME_AUTH_TOKEN ?? "";
|
const runtimeToken = process.env.CAMINO_RUNTIME_AUTH_TOKEN ?? "";
|
||||||
|
if (process.env.CAMINO_RUNTIME_AUTH_REQUIRED === "1" && !runtimeToken) {
|
||||||
|
throw new Error(
|
||||||
|
"CAMINO_RUNTIME_AUTH_REQUIRED=1 but CAMINO_RUNTIME_AUTH_TOKEN is missing",
|
||||||
|
);
|
||||||
|
}
|
||||||
const runtimeTokenInterceptor: Interceptor = (next) => async (request) => {
|
const runtimeTokenInterceptor: Interceptor = (next) => async (request) => {
|
||||||
if (runtimeToken) {
|
if (runtimeToken) {
|
||||||
request.header.set("x-camino-runtime-token", runtimeToken);
|
request.header.set("x-camino-runtime-token", runtimeToken);
|
||||||
@@ -810,11 +1007,16 @@ export const serveQuixosPackageRuntime = <
|
|||||||
|
|
||||||
async invoke(request) {
|
async invoke(request) {
|
||||||
try {
|
try {
|
||||||
const { runtimeFunction } = resolveRuntimeFunction(request.function);
|
const { spec, runtimeFunction } = resolveRuntimeFunction(request.function);
|
||||||
|
const value = await runtimeFunction(
|
||||||
|
params.createContext(trackingCamino, request),
|
||||||
|
);
|
||||||
return create(InvokeResponseSchema, {
|
return create(InvokeResponseSchema, {
|
||||||
ok: true,
|
ok: true,
|
||||||
result: jsToProtoValue(
|
result: jsToProtoValue(
|
||||||
await runtimeFunction(params.createContext(trackingCamino, request)),
|
spec.operation === "get"
|
||||||
|
? wrapFieldResult(spec, request.objectId, value)
|
||||||
|
: value,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -844,7 +1046,9 @@ export const serveQuixosPackageRuntime = <
|
|||||||
}
|
}
|
||||||
const sink: DerivedWatchSink = {
|
const sink: DerivedWatchSink = {
|
||||||
publish(event) {
|
publish(event) {
|
||||||
queue.push(derivedEmissionToProto(event));
|
queue.push(
|
||||||
|
derivedEmissionToProtoForSpec(spec, request.objectId, event),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const result = await watchSinkStorage.run(sink, async () =>
|
const result = await watchSinkStorage.run(sink, async () =>
|
||||||
|
|||||||
@@ -0,0 +1,978 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import protobuf from "protobufjs";
|
||||||
|
import type {
|
||||||
|
Cardinality,
|
||||||
|
ClassSchema,
|
||||||
|
ConflictStrategy,
|
||||||
|
FieldSchema,
|
||||||
|
FieldStorage,
|
||||||
|
FieldStorageKind,
|
||||||
|
FunctionRef,
|
||||||
|
InterfaceFieldContract,
|
||||||
|
InterfaceImplementation,
|
||||||
|
InterfaceSchema,
|
||||||
|
MethodSchema,
|
||||||
|
OperationSchema,
|
||||||
|
OperationServiceSchema,
|
||||||
|
MigrationSpec,
|
||||||
|
SchemaCompileResult,
|
||||||
|
SymbolRef,
|
||||||
|
EdgeDirectionality,
|
||||||
|
TypeRef,
|
||||||
|
} from "./schema-ir.js";
|
||||||
|
|
||||||
|
type OptionBag = Record<string, unknown>;
|
||||||
|
|
||||||
|
const CLASS_OPTION = "(camino.class)";
|
||||||
|
const INTERFACE_OPTION = "(camino.interface)";
|
||||||
|
const IMPLEMENTS_OPTION = "(camino.implements)";
|
||||||
|
const CONSTRUCTOR_OPTION = "(camino.constructor)";
|
||||||
|
const METHOD_OPTION = "(camino.method)";
|
||||||
|
const MIGRATION_OPTION = "(camino.migration)";
|
||||||
|
const CONFLICT_OPTION = "(camino.conflict)";
|
||||||
|
const EDGE_OPTION = "(camino.edge)";
|
||||||
|
const FIELD_STORAGE_OPTION = "(camino.field_storage)";
|
||||||
|
const FIELD_OPS_OPTION = "(camino.field_ops)";
|
||||||
|
const INTERFACE_FIELD_OPTION = "(camino.interface_field)";
|
||||||
|
const DISPLAY_LABEL_OPTION = "(camino.display_label)";
|
||||||
|
const IMPL_OPTION = "(camino.impl)";
|
||||||
|
const SCHEMA_NAMESPACE_OPTION = "(camino.schema_namespace)";
|
||||||
|
const SCHEMA_VERSION_OPTION = "(camino.schema_version)";
|
||||||
|
|
||||||
|
const enumName = (value: unknown) =>
|
||||||
|
String(value)
|
||||||
|
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
||||||
|
.replace(/-/g, "_")
|
||||||
|
.toUpperCase();
|
||||||
|
|
||||||
|
const lowerEnumName = (value: unknown) => enumName(value).toLowerCase();
|
||||||
|
|
||||||
|
const getOption = (
|
||||||
|
options: OptionBag | undefined,
|
||||||
|
name: string,
|
||||||
|
): unknown => {
|
||||||
|
if (!options) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (Object.hasOwn(options, name)) {
|
||||||
|
return options[name];
|
||||||
|
}
|
||||||
|
const bareName = name.replace(/^\((.*)\)$/, "$1");
|
||||||
|
if (Object.hasOwn(options, bareName)) {
|
||||||
|
return options[bareName];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getParsedOptions = (
|
||||||
|
reflection: protobuf.ReflectionObject,
|
||||||
|
): OptionBag[] => {
|
||||||
|
const parsedOptions = (reflection as unknown as { parsedOptions?: unknown })
|
||||||
|
.parsedOptions;
|
||||||
|
return Array.isArray(parsedOptions)
|
||||||
|
? parsedOptions.filter(
|
||||||
|
(option): option is OptionBag =>
|
||||||
|
Boolean(option) && typeof option === "object" && !Array.isArray(option),
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getReflectionOptions = (
|
||||||
|
reflection: protobuf.ReflectionObject,
|
||||||
|
name: string,
|
||||||
|
): unknown[] => {
|
||||||
|
const parsedValues = getParsedOptions(reflection).flatMap((option) => {
|
||||||
|
const value = getOption(option, name);
|
||||||
|
return value === undefined ? [] : [value];
|
||||||
|
});
|
||||||
|
if (parsedValues.length > 0) {
|
||||||
|
return parsedValues;
|
||||||
|
}
|
||||||
|
const value = getOption(reflection.options, name);
|
||||||
|
return value === undefined ? [] : [value];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getReflectionOption = (
|
||||||
|
reflection: protobuf.ReflectionObject,
|
||||||
|
name: string,
|
||||||
|
): unknown => getReflectionOptions(reflection, name)[0];
|
||||||
|
|
||||||
|
const asObject = (value: unknown): OptionBag | undefined =>
|
||||||
|
value && typeof value === "object" && !Array.isArray(value)
|
||||||
|
? (value as OptionBag)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const asArray = (value: unknown): unknown[] =>
|
||||||
|
Array.isArray(value) ? value : value === undefined ? [] : [value];
|
||||||
|
|
||||||
|
const readString = (
|
||||||
|
object: OptionBag | undefined,
|
||||||
|
key: string,
|
||||||
|
): string | undefined => {
|
||||||
|
const value = object?.[key];
|
||||||
|
return typeof value === "string" ? value : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const readNumber = (
|
||||||
|
object: OptionBag | undefined,
|
||||||
|
key: string,
|
||||||
|
): number | undefined => {
|
||||||
|
const value = object?.[key];
|
||||||
|
return typeof value === "number" && Number.isFinite(value)
|
||||||
|
? value
|
||||||
|
: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const readBoolean = (
|
||||||
|
object: OptionBag | undefined,
|
||||||
|
key: string,
|
||||||
|
): boolean | undefined => {
|
||||||
|
const value = object?.[key];
|
||||||
|
return typeof value === "boolean" ? value : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const symbolRefFromOption = (
|
||||||
|
value: unknown,
|
||||||
|
fallback: SymbolRef,
|
||||||
|
): SymbolRef => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
namespace: readString(object, "namespace") ?? fallback.namespace,
|
||||||
|
name: readString(object, "name") ?? fallback.name,
|
||||||
|
version: readString(object, "version") ?? fallback.version,
|
||||||
|
...(readString(object, "hash")
|
||||||
|
? { hash: readString(object, "hash") }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeRefFromOption = (
|
||||||
|
value: unknown,
|
||||||
|
fallbackVersion: string,
|
||||||
|
): TypeRef | undefined => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const symbol = asObject(object.symbol)
|
||||||
|
? symbolRefFromOption(object.symbol, {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: fallbackVersion,
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
const protoType = readString(object, "proto_type")?.replace(/^\./, "");
|
||||||
|
const symbolProtoType =
|
||||||
|
symbol?.namespace && symbol.name ? `${symbol.namespace}.${symbol.name}` : "";
|
||||||
|
const resolvedProtoType = protoType ?? symbolProtoType;
|
||||||
|
if (!resolvedProtoType) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
protoType: resolvedProtoType,
|
||||||
|
...(symbol?.namespace && symbol.name ? { symbol } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const interfaceFieldContractFromOption = (
|
||||||
|
value: unknown,
|
||||||
|
schemaVersion: string,
|
||||||
|
): InterfaceFieldContract | undefined => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const type = typeRefFromOption(object.type, schemaVersion);
|
||||||
|
const refTargetTypeParam = readString(object, "ref_target_type_param");
|
||||||
|
const edgeCardinality =
|
||||||
|
object.edge_cardinality === undefined
|
||||||
|
? undefined
|
||||||
|
: cardinalityFromOption(object.edge_cardinality);
|
||||||
|
return {
|
||||||
|
required: readBoolean(object, "required") ?? false,
|
||||||
|
readable: readBoolean(object, "readable") ?? false,
|
||||||
|
writable: readBoolean(object, "writable") ?? false,
|
||||||
|
watchable: readBoolean(object, "watchable") ?? false,
|
||||||
|
...(readString(object, "type_param")
|
||||||
|
? { typeParam: readString(object, "type_param") }
|
||||||
|
: {}),
|
||||||
|
...(type ? { type } : {}),
|
||||||
|
...(refTargetTypeParam ? { refTargetTypeParam } : {}),
|
||||||
|
...(edgeCardinality ? { edgeCardinality } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const functionRefFromOption = (value: unknown): FunctionRef => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
throw new Error("Expected function ref option object");
|
||||||
|
}
|
||||||
|
const packageNamespace = readString(object, "package_namespace");
|
||||||
|
const packageName = readString(object, "package_name");
|
||||||
|
const symbol = readString(object, "symbol");
|
||||||
|
if (!packageNamespace || !packageName || !symbol) {
|
||||||
|
throw new Error("Function ref requires package_namespace, package_name, and symbol");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
packageNamespace,
|
||||||
|
packageName,
|
||||||
|
symbol,
|
||||||
|
...(readString(object, "operation")
|
||||||
|
? { operation: readString(object, "operation") }
|
||||||
|
: {}),
|
||||||
|
...(readString(object, "version_ref")
|
||||||
|
? { versionRef: readString(object, "version_ref") }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const conflictFromOption = (value: unknown): ConflictStrategy => {
|
||||||
|
const normalized = enumName(value);
|
||||||
|
if (!value || normalized === "CONFLICT_STRATEGY_UNSPECIFIED") {
|
||||||
|
return "preserve_conflicts";
|
||||||
|
}
|
||||||
|
if (normalized === "REPLACE") {
|
||||||
|
return "replace";
|
||||||
|
}
|
||||||
|
if (normalized === "PRESERVE_CONFLICTS") {
|
||||||
|
return "preserve_conflicts";
|
||||||
|
}
|
||||||
|
if (normalized === "CRDT") {
|
||||||
|
return "crdt";
|
||||||
|
}
|
||||||
|
throw new Error(`Unknown Camino conflict strategy: ${String(value)}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cardinalityFromOption = (value: unknown): Cardinality => {
|
||||||
|
const normalized = lowerEnumName(value);
|
||||||
|
if (!value || normalized === "cardinality_unspecified") {
|
||||||
|
return "many";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
normalized === "optional_one" ||
|
||||||
|
normalized === "exactly_one" ||
|
||||||
|
normalized === "many" ||
|
||||||
|
normalized === "many_unique" ||
|
||||||
|
normalized === "many_ordered" ||
|
||||||
|
normalized === "many_unique_ordered"
|
||||||
|
) {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
throw new Error(`Unknown Camino cardinality: ${String(value)}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fieldStorageKindFromOption = (value: unknown): FieldStorageKind => {
|
||||||
|
if (value === 5) {
|
||||||
|
return "static_final";
|
||||||
|
}
|
||||||
|
const normalized = lowerEnumName(value);
|
||||||
|
if (!value || normalized === "field_storage_kind_unspecified") {
|
||||||
|
return "stored";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
normalized === "stored" ||
|
||||||
|
normalized === "derived" ||
|
||||||
|
normalized === "lazy" ||
|
||||||
|
normalized === "external" ||
|
||||||
|
normalized === "static_final"
|
||||||
|
) {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
throw new Error(`Unknown Camino field storage kind: ${String(value)}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fieldStorageFromOption = (value: unknown): FieldStorage => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return {
|
||||||
|
kind: "stored",
|
||||||
|
cache: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const kind = fieldStorageKindFromOption(object.kind);
|
||||||
|
const resolver = object.resolver === undefined
|
||||||
|
? undefined
|
||||||
|
: functionRefFromOption(object.resolver);
|
||||||
|
if ((kind === "derived" || kind === "lazy" || kind === "external") && !resolver) {
|
||||||
|
throw new Error(`${kind} field storage requires resolver`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
kind,
|
||||||
|
...(resolver ? { resolver } : {}),
|
||||||
|
cache: object.cache === true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const fieldOpsFromOption = (value: unknown) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const implementation = enumName(object.implementation);
|
||||||
|
if (implementation !== "SERVICE") {
|
||||||
|
throw new Error(
|
||||||
|
`Unsupported Camino field implementation: ${String(object.implementation)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const service = readString(object, "service");
|
||||||
|
if (!service) {
|
||||||
|
throw new Error("SERVICE field_ops requires service");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
implementation: "service" as const,
|
||||||
|
service,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractFileDefaults = (sourceFile: string) => {
|
||||||
|
const text = fs.readFileSync(sourceFile, "utf8");
|
||||||
|
const packageMatch = /^\s*package\s+([a-zA-Z0-9_.]+)\s*;/m.exec(text);
|
||||||
|
const namespaceMatch =
|
||||||
|
/^\s*option\s+\(camino\.schema_namespace\)\s*=\s*"([^"]+)"\s*;/m.exec(
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
const versionMatch =
|
||||||
|
/^\s*option\s+\(camino\.schema_version\)\s*=\s*"([^"]+)"\s*;/m.exec(text);
|
||||||
|
|
||||||
|
return {
|
||||||
|
packageName: packageMatch?.[1] ?? "",
|
||||||
|
schemaNamespace: namespaceMatch?.[1] ?? packageMatch?.[1] ?? "",
|
||||||
|
schemaVersion: versionMatch?.[1] ?? "1",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const protoSymbolFromReflection = (
|
||||||
|
reflection: protobuf.ReflectionObject | null,
|
||||||
|
fallbackVersion: string,
|
||||||
|
): SymbolRef | undefined => {
|
||||||
|
if (!reflection?.fullName) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const parts = reflection.fullName.replace(/^\./, "").split(".");
|
||||||
|
const name = parts.pop();
|
||||||
|
if (!name) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
namespace: parts.join("."),
|
||||||
|
name,
|
||||||
|
version: fallbackVersion,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeRefForField = (
|
||||||
|
field: protobuf.Field,
|
||||||
|
schemaVersion: string,
|
||||||
|
): TypeRef => {
|
||||||
|
const symbol = protoSymbolFromReflection(field.resolvedType, schemaVersion);
|
||||||
|
const enumValues =
|
||||||
|
field.resolvedType instanceof protobuf.Enum
|
||||||
|
? Object.entries(field.resolvedType.values)
|
||||||
|
.filter(([_name, value]) => value !== 0)
|
||||||
|
.map(([name]) => name)
|
||||||
|
: undefined;
|
||||||
|
return {
|
||||||
|
protoType: field.type,
|
||||||
|
...(symbol ? { symbol } : {}),
|
||||||
|
...(enumValues ? { enumValues } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeRefForType = (
|
||||||
|
type: protobuf.Type | undefined,
|
||||||
|
fallbackType: string,
|
||||||
|
schemaVersion: string,
|
||||||
|
): TypeRef => {
|
||||||
|
const symbol = protoSymbolFromReflection(type ?? null, schemaVersion);
|
||||||
|
return {
|
||||||
|
protoType: type?.fullName?.replace(/^\./, "") ?? fallbackType,
|
||||||
|
...(symbol ? { symbol } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const fieldSchemaFromField = (
|
||||||
|
field: protobuf.Field,
|
||||||
|
schemaVersion: string,
|
||||||
|
): FieldSchema => {
|
||||||
|
const storage = fieldStorageFromOption(
|
||||||
|
getReflectionOption(field, FIELD_STORAGE_OPTION),
|
||||||
|
);
|
||||||
|
const ops = fieldOpsFromOption(getReflectionOption(field, FIELD_OPS_OPTION));
|
||||||
|
if (ops && storage.kind !== "stored" && storage.kind !== "static_final") {
|
||||||
|
throw new Error(
|
||||||
|
`Field ${field.fullName} cannot declare both field_ops and package-resolved storage`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (storage.kind === "static_final" && !ops) {
|
||||||
|
throw new Error(`Field ${field.fullName} with static_final storage requires field_ops`);
|
||||||
|
}
|
||||||
|
const interfaceContract = interfaceFieldContractFromOption(
|
||||||
|
getReflectionOption(field, INTERFACE_FIELD_OPTION),
|
||||||
|
schemaVersion,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
name: field.name,
|
||||||
|
tag: field.id,
|
||||||
|
type: typeRefForField(field, schemaVersion),
|
||||||
|
conflict: conflictFromOption(getReflectionOption(field, CONFLICT_OPTION)),
|
||||||
|
repeated: field.repeated,
|
||||||
|
optional: field.optional,
|
||||||
|
storage,
|
||||||
|
...(ops ? { ops } : {}),
|
||||||
|
...(interfaceContract ? { interfaceContract } : {}),
|
||||||
|
isDisplayLabel: getReflectionOption(field, DISPLAY_LABEL_OPTION) === true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const isManyCardinality = (cardinality: Cardinality) =>
|
||||||
|
cardinality === "many" ||
|
||||||
|
cardinality === "many_unique" ||
|
||||||
|
cardinality === "many_ordered" ||
|
||||||
|
cardinality === "many_unique_ordered";
|
||||||
|
|
||||||
|
const materializationFromOption = (
|
||||||
|
value: unknown,
|
||||||
|
fallbackVersion: string,
|
||||||
|
) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const classRef = symbolRefFromOption(object.class, {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: fallbackVersion,
|
||||||
|
});
|
||||||
|
if (!classRef.name) {
|
||||||
|
throw new Error("Edge materialization requires class");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
class: classRef,
|
||||||
|
connectProjection: readString(object, "connect_projection") ?? "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const endpointFromOption = (params: {
|
||||||
|
option: unknown;
|
||||||
|
fallbackClass?: SymbolRef;
|
||||||
|
fallbackProjection: string;
|
||||||
|
fallbackCardinality: Cardinality;
|
||||||
|
fallbackVersion: string;
|
||||||
|
}) => {
|
||||||
|
const object = asObject(params.option);
|
||||||
|
const classRef = asObject(object?.class)
|
||||||
|
? symbolRefFromOption(object?.class, {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: params.fallbackVersion,
|
||||||
|
})
|
||||||
|
: params.fallbackClass;
|
||||||
|
const interfaceRef = asObject(object?.interface)
|
||||||
|
? symbolRefFromOption(object?.interface, {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: params.fallbackVersion,
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
const projection = readString(object, "projection") ?? params.fallbackProjection;
|
||||||
|
const cardinality = object?.cardinality === undefined
|
||||||
|
? params.fallbackCardinality
|
||||||
|
: cardinalityFromOption(object.cardinality);
|
||||||
|
const materialization = materializationFromOption(
|
||||||
|
object?.materialize,
|
||||||
|
params.fallbackVersion,
|
||||||
|
);
|
||||||
|
if (!classRef?.name && !interfaceRef?.name) {
|
||||||
|
throw new Error(`Edge endpoint ${projection} requires class or interface`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...(classRef?.name ? { class: classRef } : {}),
|
||||||
|
...(interfaceRef?.name ? { interface: interfaceRef } : {}),
|
||||||
|
projection,
|
||||||
|
cardinality,
|
||||||
|
indexed: readBoolean(object, "indexed") ?? false,
|
||||||
|
...(materialization ? { materialization } : {}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const symbolKey = (ref: SymbolRef | undefined) =>
|
||||||
|
ref ? [ref.namespace, ref.name, ref.version, ref.hash ?? ""].join(":") : "";
|
||||||
|
|
||||||
|
const endpointRoleKey = (endpoint: ReturnType<typeof endpointFromOption>) =>
|
||||||
|
[
|
||||||
|
symbolKey(endpoint.class),
|
||||||
|
symbolKey(endpoint.interface),
|
||||||
|
endpoint.projection,
|
||||||
|
endpoint.cardinality,
|
||||||
|
].join("|");
|
||||||
|
|
||||||
|
const normalizeEdgeEndpoints = (params: {
|
||||||
|
directionality: "directed" | "undirected";
|
||||||
|
fromEndpoint: ReturnType<typeof endpointFromOption>;
|
||||||
|
toEndpoint: ReturnType<typeof endpointFromOption>;
|
||||||
|
}) => {
|
||||||
|
if (params.directionality === "directed") {
|
||||||
|
return {
|
||||||
|
fromEndpoint: params.fromEndpoint,
|
||||||
|
toEndpoint: params.toEndpoint,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return endpointRoleKey(params.fromEndpoint) <= endpointRoleKey(params.toEndpoint)
|
||||||
|
? {
|
||||||
|
fromEndpoint: params.fromEndpoint,
|
||||||
|
toEndpoint: params.toEndpoint,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fromEndpoint: params.toEndpoint,
|
||||||
|
toEndpoint: params.fromEndpoint,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const symbolArrayFromOption = (
|
||||||
|
value: unknown,
|
||||||
|
defaults: ReturnType<typeof extractFileDefaults>,
|
||||||
|
) =>
|
||||||
|
asArray(value)
|
||||||
|
.map((item) =>
|
||||||
|
symbolRefFromOption(item, {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: "",
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.filter((symbol) => Boolean(symbol.name));
|
||||||
|
|
||||||
|
const isStringType = (type: TypeRef) =>
|
||||||
|
type.protoType === "string" || type.protoType === "google.protobuf.StringValue";
|
||||||
|
|
||||||
|
const validateDisplayLabelFields = (
|
||||||
|
type: protobuf.Type,
|
||||||
|
fields: FieldSchema[],
|
||||||
|
operationServices: OperationServiceSchema[],
|
||||||
|
) => {
|
||||||
|
const displayLabelFields = fields.filter((field) => field.isDisplayLabel);
|
||||||
|
if (displayLabelFields.length > 1) {
|
||||||
|
throw new Error(
|
||||||
|
`Class ${type.fullName} declares multiple Camino display label fields`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const displayLabelField = displayLabelFields[0];
|
||||||
|
if (!displayLabelField) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!displayLabelField.ops) {
|
||||||
|
if (!isStringType(displayLabelField.type)) {
|
||||||
|
throw new Error(
|
||||||
|
`Display label field ${type.fullName}.${displayLabelField.name} must have string type`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const serviceName = displayLabelField.ops.service.replace(/^\./, "");
|
||||||
|
const getOperation = operationServices
|
||||||
|
.find((service) => service.fullName === serviceName)
|
||||||
|
?.operations.find((operation) => operation.name === "Get");
|
||||||
|
if (!getOperation || !isStringType(getOperation.outputType)) {
|
||||||
|
throw new Error(
|
||||||
|
`Display label field ${type.fullName}.${displayLabelField.name} must resolve to string`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeBindingFromOption = (
|
||||||
|
value: unknown,
|
||||||
|
schemaVersion: string,
|
||||||
|
) => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const name = readString(object, "name");
|
||||||
|
const type = typeRefFromOption(object.type, schemaVersion);
|
||||||
|
if (!name || !type) {
|
||||||
|
throw new Error("Interface type_binding requires name and type");
|
||||||
|
}
|
||||||
|
return { name, type };
|
||||||
|
};
|
||||||
|
|
||||||
|
const interfaceImplementationFromOption = (
|
||||||
|
value: unknown,
|
||||||
|
defaults: ReturnType<typeof extractFileDefaults>,
|
||||||
|
): InterfaceImplementation => {
|
||||||
|
const object = asObject(value);
|
||||||
|
if (!object) {
|
||||||
|
throw new Error("Expected implements option object");
|
||||||
|
}
|
||||||
|
const interfaceId = symbolRefFromOption(object.interface, {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: "",
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
if (!interfaceId.name) {
|
||||||
|
throw new Error("implements option requires interface name");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
interface: interfaceId,
|
||||||
|
typeBindings: asArray(object.type_binding)
|
||||||
|
.map((binding) => typeBindingFromOption(binding, defaults.schemaVersion))
|
||||||
|
.filter((binding): binding is NonNullable<typeof binding> =>
|
||||||
|
Boolean(binding),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const serviceName = (service: protobuf.Service) =>
|
||||||
|
service.fullName.replace(/^\./, "");
|
||||||
|
|
||||||
|
const operationFromMethod = (
|
||||||
|
method: protobuf.Method,
|
||||||
|
schemaVersion: string,
|
||||||
|
): OperationSchema => {
|
||||||
|
const fn = functionRefFromOption(getReflectionOption(method, IMPL_OPTION));
|
||||||
|
const methodWithTypes = method as protobuf.Method & {
|
||||||
|
resolvedRequestType?: protobuf.Type;
|
||||||
|
resolvedResponseType?: protobuf.Type;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
name: method.name,
|
||||||
|
inputType: typeRefForType(
|
||||||
|
methodWithTypes.resolvedRequestType,
|
||||||
|
method.requestType,
|
||||||
|
schemaVersion,
|
||||||
|
),
|
||||||
|
outputType: typeRefForType(
|
||||||
|
methodWithTypes.resolvedResponseType,
|
||||||
|
method.responseType,
|
||||||
|
schemaVersion,
|
||||||
|
),
|
||||||
|
function: fn,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const operationServiceFromService = (
|
||||||
|
service: protobuf.Service,
|
||||||
|
schemaVersion: string,
|
||||||
|
): OperationServiceSchema => ({
|
||||||
|
name: service.name,
|
||||||
|
fullName: serviceName(service),
|
||||||
|
operations: service.methodsArray.map((method) =>
|
||||||
|
operationFromMethod(method, schemaVersion),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const classSchemaFromType = (
|
||||||
|
type: protobuf.Type,
|
||||||
|
sourceFile: string,
|
||||||
|
defaults: ReturnType<typeof extractFileDefaults>,
|
||||||
|
operationServices: OperationServiceSchema[],
|
||||||
|
): ClassSchema | undefined => {
|
||||||
|
const classOption = asObject(getReflectionOption(type, CLASS_OPTION));
|
||||||
|
if (!classOption) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackClassRef = {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: type.name,
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
};
|
||||||
|
const classId = symbolRefFromOption(classOption.id, fallbackClassRef);
|
||||||
|
const classVersion = readNumber(classOption, "version") ?? Number(classId.version);
|
||||||
|
if (!Number.isInteger(classVersion) || classVersion <= 0) {
|
||||||
|
throw new Error(`Class ${type.fullName} has invalid Camino class version`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = type.fieldsArray.map((field) =>
|
||||||
|
fieldSchemaFromField(field, defaults.schemaVersion),
|
||||||
|
);
|
||||||
|
validateDisplayLabelFields(type, fields, operationServices);
|
||||||
|
const edges = type.fieldsArray.flatMap((field) => {
|
||||||
|
const edgeOption = asObject(getReflectionOption(field, EDGE_OPTION));
|
||||||
|
if (!edgeOption) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const legacyTargetClass = symbolRefFromOption(edgeOption.target, {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: "",
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
const cardinality = cardinalityFromOption(edgeOption.cardinality);
|
||||||
|
const declaredFromEndpoint = endpointFromOption({
|
||||||
|
option: edgeOption.this_endpoint,
|
||||||
|
fallbackClass: classId,
|
||||||
|
fallbackProjection: field.name,
|
||||||
|
fallbackCardinality: cardinality,
|
||||||
|
fallbackVersion: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
const declaredToEndpoint = endpointFromOption({
|
||||||
|
option: edgeOption.other_endpoint,
|
||||||
|
fallbackClass: legacyTargetClass.name ? legacyTargetClass : undefined,
|
||||||
|
fallbackProjection: readString(edgeOption, "inverse") ?? "",
|
||||||
|
fallbackCardinality: "many",
|
||||||
|
fallbackVersion: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
if (!declaredToEndpoint.projection) {
|
||||||
|
throw new Error(
|
||||||
|
`Edge field ${type.fullName}.${field.name} requires other_endpoint.projection or inverse`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const sourceType = typeRefForField(field, defaults.schemaVersion);
|
||||||
|
if (
|
||||||
|
sourceType.symbol?.namespace !== "camino" ||
|
||||||
|
sourceType.symbol.name !== "Ref"
|
||||||
|
) {
|
||||||
|
throw new Error(`Edge field ${type.fullName}.${field.name} must use camino.Ref`);
|
||||||
|
}
|
||||||
|
const isMany = isManyCardinality(declaredFromEndpoint.cardinality);
|
||||||
|
if (isMany && !field.repeated) {
|
||||||
|
throw new Error(`Edge field ${type.fullName}.${field.name} must be repeated for ${declaredFromEndpoint.cardinality}`);
|
||||||
|
}
|
||||||
|
if (!isMany && field.repeated) {
|
||||||
|
throw new Error(`Edge field ${type.fullName}.${field.name} must not be repeated for ${declaredFromEndpoint.cardinality}`);
|
||||||
|
}
|
||||||
|
const directionality: EdgeDirectionality =
|
||||||
|
edgeOption.undirected === true ? "undirected" : "directed";
|
||||||
|
const { fromEndpoint, toEndpoint } = normalizeEdgeEndpoints({
|
||||||
|
directionality,
|
||||||
|
fromEndpoint: declaredFromEndpoint,
|
||||||
|
toEndpoint: declaredToEndpoint,
|
||||||
|
});
|
||||||
|
const edgeId = symbolRefFromOption(edgeOption.id, {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: `${type.name}.${field.name}`,
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
});
|
||||||
|
const fallbackToClass = toEndpoint.class ?? {
|
||||||
|
namespace: "",
|
||||||
|
name: "",
|
||||||
|
version: "",
|
||||||
|
};
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: edgeId,
|
||||||
|
directionality,
|
||||||
|
sourceField: fromEndpoint.projection,
|
||||||
|
fromClass: fromEndpoint.class ?? classId,
|
||||||
|
toClass: fallbackToClass,
|
||||||
|
cardinality: fromEndpoint.cardinality,
|
||||||
|
...(toEndpoint.projection
|
||||||
|
? { inverse: toEndpoint.projection }
|
||||||
|
: {}),
|
||||||
|
fields: [],
|
||||||
|
fromEndpoint,
|
||||||
|
toEndpoint,
|
||||||
|
declaringClass: classId,
|
||||||
|
tags: symbolArrayFromOption(edgeOption.tag, defaults),
|
||||||
|
implements: symbolArrayFromOption(edgeOption.implements, defaults),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const methods: MethodSchema[] = getReflectionOptions(type, METHOD_OPTION)
|
||||||
|
.map(asObject)
|
||||||
|
.filter((option): option is OptionBag => Boolean(option))
|
||||||
|
.map((option) => {
|
||||||
|
const name = readString(option, "name");
|
||||||
|
if (!name) {
|
||||||
|
throw new Error(`Method option on ${type.fullName} requires name`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
function: functionRefFromOption(option.function),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const migrations: MigrationSpec[] = getReflectionOptions(type, MIGRATION_OPTION)
|
||||||
|
.map(asObject)
|
||||||
|
.filter((option): option is OptionBag => Boolean(option))
|
||||||
|
.map((option) => {
|
||||||
|
const fromVersion = readNumber(option, "from_version");
|
||||||
|
const toVersion = readNumber(option, "to_version");
|
||||||
|
if (!fromVersion || !toVersion) {
|
||||||
|
throw new Error(`Migration option on ${type.fullName} requires versions`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
fromVersion,
|
||||||
|
toVersion,
|
||||||
|
function: functionRefFromOption(option.function),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const constructorOption = asObject(
|
||||||
|
getReflectionOption(type, CONSTRUCTOR_OPTION),
|
||||||
|
);
|
||||||
|
const constructorSpec = constructorOption
|
||||||
|
? { function: functionRefFromOption(constructorOption.function) }
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: classId,
|
||||||
|
version: classVersion,
|
||||||
|
fields,
|
||||||
|
edges,
|
||||||
|
methods,
|
||||||
|
migrations,
|
||||||
|
operationServices,
|
||||||
|
implements: getReflectionOptions(type, IMPLEMENTS_OPTION).map((option) =>
|
||||||
|
interfaceImplementationFromOption(option, defaults),
|
||||||
|
),
|
||||||
|
...(constructorSpec ? { constructorSpec } : {}),
|
||||||
|
sourceFile,
|
||||||
|
protoMessage: type.fullName,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const interfaceSchemaFromType = (
|
||||||
|
type: protobuf.Type,
|
||||||
|
sourceFile: string,
|
||||||
|
defaults: ReturnType<typeof extractFileDefaults>,
|
||||||
|
): InterfaceSchema | undefined => {
|
||||||
|
const interfaceOption = asObject(getReflectionOption(type, INTERFACE_OPTION));
|
||||||
|
if (!interfaceOption) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const classOption = asObject(getReflectionOption(type, CLASS_OPTION));
|
||||||
|
if (classOption) {
|
||||||
|
throw new Error(`${type.fullName} cannot be both a Camino class and interface`);
|
||||||
|
}
|
||||||
|
const fallbackInterfaceRef = {
|
||||||
|
namespace: defaults.schemaNamespace,
|
||||||
|
name: type.name,
|
||||||
|
version: defaults.schemaVersion,
|
||||||
|
};
|
||||||
|
const interfaceId = symbolRefFromOption(
|
||||||
|
interfaceOption.id,
|
||||||
|
fallbackInterfaceRef,
|
||||||
|
);
|
||||||
|
const interfaceVersion =
|
||||||
|
readNumber(interfaceOption, "version") ?? Number(interfaceId.version);
|
||||||
|
if (!Number.isInteger(interfaceVersion) || interfaceVersion <= 0) {
|
||||||
|
throw new Error(`Interface ${type.fullName} has invalid Camino interface version`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: interfaceId,
|
||||||
|
version: interfaceVersion,
|
||||||
|
fields: type.fieldsArray.map((field) =>
|
||||||
|
fieldSchemaFromField(field, defaults.schemaVersion),
|
||||||
|
),
|
||||||
|
sourceFile,
|
||||||
|
protoMessage: type.fullName,
|
||||||
|
typeParameters: asArray(interfaceOption.type_parameter)
|
||||||
|
.map((entry) => String(entry).trim())
|
||||||
|
.filter(Boolean),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const walkTypes = (
|
||||||
|
namespace: protobuf.NamespaceBase,
|
||||||
|
visit: (type: protobuf.Type) => void,
|
||||||
|
) => {
|
||||||
|
for (const nested of namespace.nestedArray) {
|
||||||
|
if (nested instanceof protobuf.Type) {
|
||||||
|
visit(nested);
|
||||||
|
walkTypes(nested, visit);
|
||||||
|
} else if (nested instanceof protobuf.Namespace) {
|
||||||
|
walkTypes(nested, visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const walkServices = (
|
||||||
|
namespace: protobuf.NamespaceBase,
|
||||||
|
visit: (service: protobuf.Service) => void,
|
||||||
|
) => {
|
||||||
|
for (const nested of namespace.nestedArray) {
|
||||||
|
if (nested instanceof protobuf.Service) {
|
||||||
|
visit(nested);
|
||||||
|
} else if (nested instanceof protobuf.Namespace) {
|
||||||
|
walkServices(nested, visit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadCaminoSchemaRoot = async (
|
||||||
|
sourceFile: string,
|
||||||
|
): Promise<{ absoluteSourceFile: string; root: protobuf.Root }> => {
|
||||||
|
const absoluteSourceFile = path.resolve(sourceFile);
|
||||||
|
const root = new protobuf.Root();
|
||||||
|
const envIncludeDirs = [
|
||||||
|
process.env.QUIXOS_PROTO_PATH,
|
||||||
|
process.env.CAMINO_PROTO_PATH,
|
||||||
|
]
|
||||||
|
.filter((value): value is string => Boolean(value))
|
||||||
|
.join(path.delimiter)
|
||||||
|
.split(path.delimiter)
|
||||||
|
.map((entry) => entry.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((entry) => path.resolve(entry));
|
||||||
|
const includeDirs = [
|
||||||
|
path.dirname(absoluteSourceFile),
|
||||||
|
path.resolve("../../quixos-protocol/proto"),
|
||||||
|
path.resolve("quixos-protocol/proto"),
|
||||||
|
path.resolve("proto"),
|
||||||
|
...envIncludeDirs,
|
||||||
|
];
|
||||||
|
root.resolvePath = (origin, target) => {
|
||||||
|
if (path.isAbsolute(target) && fs.existsSync(target)) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
const originDir = origin ? path.dirname(origin) : undefined;
|
||||||
|
const candidates = [
|
||||||
|
...(originDir ? [path.resolve(originDir, target)] : []),
|
||||||
|
...includeDirs.map((dir) => path.resolve(dir, target)),
|
||||||
|
];
|
||||||
|
const found = candidates.find((candidate) => fs.existsSync(candidate));
|
||||||
|
return found ?? target;
|
||||||
|
};
|
||||||
|
|
||||||
|
await root.load(absoluteSourceFile, { keepCase: true });
|
||||||
|
root.resolveAll();
|
||||||
|
|
||||||
|
return { absoluteSourceFile, root };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const compileCaminoSchema = async (
|
||||||
|
sourceFile: string,
|
||||||
|
): Promise<SchemaCompileResult> => {
|
||||||
|
const { absoluteSourceFile, root } = await loadCaminoSchemaRoot(sourceFile);
|
||||||
|
const defaults = extractFileDefaults(absoluteSourceFile);
|
||||||
|
|
||||||
|
const operationServices: OperationServiceSchema[] = [];
|
||||||
|
walkServices(root, (service) => {
|
||||||
|
operationServices.push(
|
||||||
|
operationServiceFromService(service, defaults.schemaVersion),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const classes: ClassSchema[] = [];
|
||||||
|
const interfaces: InterfaceSchema[] = [];
|
||||||
|
walkTypes(root, (type) => {
|
||||||
|
const interfaceSchema = interfaceSchemaFromType(
|
||||||
|
type,
|
||||||
|
absoluteSourceFile,
|
||||||
|
defaults,
|
||||||
|
);
|
||||||
|
if (interfaceSchema) {
|
||||||
|
interfaces.push(interfaceSchema);
|
||||||
|
}
|
||||||
|
const classSchema = classSchemaFromType(
|
||||||
|
type,
|
||||||
|
absoluteSourceFile,
|
||||||
|
defaults,
|
||||||
|
operationServices,
|
||||||
|
);
|
||||||
|
if (classSchema) {
|
||||||
|
classes.push(classSchema);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceFile: absoluteSourceFile,
|
||||||
|
classes,
|
||||||
|
interfaces,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
export type SymbolRef = {
|
||||||
|
namespace: string;
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
hash?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FunctionRef = {
|
||||||
|
packageNamespace: string;
|
||||||
|
packageName: string;
|
||||||
|
symbol: string;
|
||||||
|
operation?: string;
|
||||||
|
versionRef?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConflictStrategy =
|
||||||
|
| "replace"
|
||||||
|
| "preserve_conflicts"
|
||||||
|
| "crdt";
|
||||||
|
|
||||||
|
export type Cardinality =
|
||||||
|
| "optional_one"
|
||||||
|
| "exactly_one"
|
||||||
|
| "many"
|
||||||
|
| "many_unique"
|
||||||
|
| "many_ordered"
|
||||||
|
| "many_unique_ordered";
|
||||||
|
|
||||||
|
export type EdgeDirectionality = "directed" | "undirected";
|
||||||
|
|
||||||
|
export type EdgeMaterialization = {
|
||||||
|
class: SymbolRef;
|
||||||
|
connectProjection: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FieldStorageKind =
|
||||||
|
| "stored"
|
||||||
|
| "derived"
|
||||||
|
| "lazy"
|
||||||
|
| "external"
|
||||||
|
| "static_final";
|
||||||
|
|
||||||
|
export type FieldStorage = {
|
||||||
|
kind: FieldStorageKind;
|
||||||
|
resolver?: FunctionRef;
|
||||||
|
cache: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FieldOps = {
|
||||||
|
implementation: "service";
|
||||||
|
service: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TypeRef = {
|
||||||
|
protoType: string;
|
||||||
|
symbol?: SymbolRef;
|
||||||
|
enumValues?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TypeBinding = {
|
||||||
|
name: string;
|
||||||
|
type: TypeRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InterfaceFieldContract = {
|
||||||
|
required: boolean;
|
||||||
|
readable: boolean;
|
||||||
|
writable: boolean;
|
||||||
|
watchable: boolean;
|
||||||
|
typeParam?: string;
|
||||||
|
type?: TypeRef;
|
||||||
|
refTargetTypeParam?: string;
|
||||||
|
edgeCardinality?: Cardinality;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InterfaceImplementation = {
|
||||||
|
interface: SymbolRef;
|
||||||
|
typeBindings: TypeBinding[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FieldSchema = {
|
||||||
|
name: string;
|
||||||
|
tag: number;
|
||||||
|
type: TypeRef;
|
||||||
|
conflict: ConflictStrategy;
|
||||||
|
repeated: boolean;
|
||||||
|
optional: boolean;
|
||||||
|
storage: FieldStorage;
|
||||||
|
ops?: FieldOps;
|
||||||
|
interfaceContract?: InterfaceFieldContract;
|
||||||
|
isDisplayLabel: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EdgeEndpointSchema = {
|
||||||
|
class?: SymbolRef;
|
||||||
|
interface?: SymbolRef;
|
||||||
|
projection: string;
|
||||||
|
cardinality: Cardinality;
|
||||||
|
indexed: boolean;
|
||||||
|
materialization?: EdgeMaterialization;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EdgeSchema = {
|
||||||
|
id: SymbolRef;
|
||||||
|
directionality: EdgeDirectionality;
|
||||||
|
sourceField: string;
|
||||||
|
fromClass: SymbolRef;
|
||||||
|
toClass: SymbolRef;
|
||||||
|
cardinality: Cardinality;
|
||||||
|
inverse?: string;
|
||||||
|
fields: FieldSchema[];
|
||||||
|
fromEndpoint: EdgeEndpointSchema;
|
||||||
|
toEndpoint: EdgeEndpointSchema;
|
||||||
|
declaringClass: SymbolRef;
|
||||||
|
tags: SymbolRef[];
|
||||||
|
implements: SymbolRef[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MethodSchema = {
|
||||||
|
name: string;
|
||||||
|
function: FunctionRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MigrationSpec = {
|
||||||
|
fromVersion: number;
|
||||||
|
toVersion: number;
|
||||||
|
function: FunctionRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConstructorSpec = {
|
||||||
|
function: FunctionRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OperationSchema = {
|
||||||
|
name: string;
|
||||||
|
inputType: TypeRef;
|
||||||
|
outputType: TypeRef;
|
||||||
|
function: FunctionRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OperationServiceSchema = {
|
||||||
|
name: string;
|
||||||
|
fullName: string;
|
||||||
|
operations: OperationSchema[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ClassSchema = {
|
||||||
|
id: SymbolRef;
|
||||||
|
version: number;
|
||||||
|
fields: FieldSchema[];
|
||||||
|
edges: EdgeSchema[];
|
||||||
|
methods: MethodSchema[];
|
||||||
|
migrations: MigrationSpec[];
|
||||||
|
operationServices: OperationServiceSchema[];
|
||||||
|
implements: InterfaceImplementation[];
|
||||||
|
constructorSpec?: ConstructorSpec;
|
||||||
|
sourceFile: string;
|
||||||
|
protoMessage: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InterfaceSchema = {
|
||||||
|
id: SymbolRef;
|
||||||
|
version: number;
|
||||||
|
fields: FieldSchema[];
|
||||||
|
sourceFile: string;
|
||||||
|
protoMessage: string;
|
||||||
|
typeParameters: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SchemaCompileResult = {
|
||||||
|
sourceFile: string;
|
||||||
|
classes: ClassSchema[];
|
||||||
|
interfaces: InterfaceSchema[];
|
||||||
|
};
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
"outDir": "dist"
|
"outDir": "dist"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"]
|
"include": ["src/**/*.ts"]
|
||||||
|
|||||||
@@ -156,20 +156,35 @@ let
|
|||||||
overriddenProject = optionalOverride overrideAttrs project;
|
overriddenProject = optionalOverride overrideAttrs project;
|
||||||
|
|
||||||
cacheEntries = {
|
cacheEntries = {
|
||||||
|
"@automerge/automerge@npm:3.3.0" = { filename = "@automerge-automerge-npm-3.3.0-3f6661ca17-74b3a8ef9e.zip"; hash = "sha512-dLOo7548v0B2DomHJ+za1RKOUv0rBTgJRUrnsrhbTsZHw8uipjXNv4MfIxVzGtQl2jenhLeb8TvIbsKKfpKv8g=="; };
|
||||||
"@bufbuild/protobuf@npm:2.12.1" = { filename = "@bufbuild-protobuf-npm-2.12.1-d8afb467b3-2d40cfe4ec.zip"; hash = "sha512-LUDP5Ow7sOPwarLih3HcXcVm6qywVfvok9dWIbBAfQJy1I9ApJCtR97YOe1UFRThr3G7E68CSzrjbvDFbVbdqQ=="; };
|
"@bufbuild/protobuf@npm:2.12.1" = { filename = "@bufbuild-protobuf-npm-2.12.1-d8afb467b3-2d40cfe4ec.zip"; hash = "sha512-LUDP5Ow7sOPwarLih3HcXcVm6qywVfvok9dWIbBAfQJy1I9ApJCtR97YOe1UFRThr3G7E68CSzrjbvDFbVbdqQ=="; };
|
||||||
"@bufbuild/protoc-gen-es@npm:2.12.1" = { filename = "@bufbuild-protoc-gen-es-npm-2.12.1-6e9387fcb8-0e2805376d.zip"; hash = "sha512-DigFN22Lmj/OD9OdkPZa4dH/vqMSvi2tk2f3xyEqvFHcKleC531iqPhh7De9xtG9MM+sVRDRJCeCzXA/kH4f9Q=="; };
|
"@bufbuild/protoc-gen-es@npm:2.12.1" = { filename = "@bufbuild-protoc-gen-es-npm-2.12.1-6e9387fcb8-0e2805376d.zip"; hash = "sha512-DigFN22Lmj/OD9OdkPZa4dH/vqMSvi2tk2f3xyEqvFHcKleC531iqPhh7De9xtG9MM+sVRDRJCeCzXA/kH4f9Q=="; };
|
||||||
"@bufbuild/protoplugin@npm:2.12.1" = { filename = "@bufbuild-protoplugin-npm-2.12.1-e1aebb80bf-c7b401ab31.zip"; hash = "sha512-x7QBqzHZSsaCXLm75c/3XQXSjuINRnskt6XUKZuaygZmhuo5t6OoRTuLWgGD+qd4HoP8LliAZhgHudMVX3Hr6Q=="; };
|
"@bufbuild/protoplugin@npm:2.12.1" = { filename = "@bufbuild-protoplugin-npm-2.12.1-e1aebb80bf-c7b401ab31.zip"; hash = "sha512-x7QBqzHZSsaCXLm75c/3XQXSjuINRnskt6XUKZuaygZmhuo5t6OoRTuLWgGD+qd4HoP8LliAZhgHudMVX3Hr6Q=="; };
|
||||||
"@connectrpc/connect-node@npm:2.1.2" = { filename = "@connectrpc-connect-node-npm-2.1.2-021434d5d5-ba8b0c9384.zip"; hash = "sha512-uosMk4TDgps3eqeoJtwM1Qpgk1J9LscBRgSRKDYzbqlgSitazqQrEEd/i+RWnknd7WbKD8d/X1sCcDXgKeE+qQ=="; };
|
"@connectrpc/connect-node@npm:2.1.2" = { filename = "@connectrpc-connect-node-npm-2.1.2-021434d5d5-ba8b0c9384.zip"; hash = "sha512-uosMk4TDgps3eqeoJtwM1Qpgk1J9LscBRgSRKDYzbqlgSitazqQrEEd/i+RWnknd7WbKD8d/X1sCcDXgKeE+qQ=="; };
|
||||||
"@connectrpc/connect@npm:2.1.2" = { filename = "@connectrpc-connect-npm-2.1.2-ab34825de3-4e15000890.zip"; hash = "sha512-ThUACJAjt1Lv1E+pQKWuHh+gDhvrxLoM7ScruTWSDYw7hbd0UOZErUQruMqaJ1sMFxNviWuo7/A2F4Ex2wHWFQ=="; };
|
"@connectrpc/connect@npm:2.1.2" = { filename = "@connectrpc-connect-npm-2.1.2-ab34825de3-4e15000890.zip"; hash = "sha512-ThUACJAjt1Lv1E+pQKWuHh+gDhvrxLoM7ScruTWSDYw7hbd0UOZErUQruMqaJ1sMFxNviWuo7/A2F4Ex2wHWFQ=="; };
|
||||||
|
"@protobufjs/aspromise@npm:1.1.2" = { filename = "@protobufjs-aspromise-npm-1.1.2-71d00b938f-a83343a468.zip"; hash = "sha512-qDNDpGj/W17Gv/Nv14imTIOeSKB/+fT4E1ZPWMr0TQEc1lBO0hR780g1vXp90hBwUq91WWHGsJj9iQK09lANDw=="; };
|
||||||
|
"@protobufjs/base64@npm:1.1.2" = { filename = "@protobufjs-base64-npm-1.1.2-cd8ca6814a-eec925e681.zip"; hash = "sha512-7skl5oEIGvGQuO4jH5utMQHhiau8GC/yedprUx59vSpW8fMG83qAsb6eAKotJxaQ0I3MXzJvccnu2FRmdcjK9g=="; };
|
||||||
|
"@protobufjs/codegen@npm:2.0.5" = { filename = "@protobufjs-codegen-npm-2.0.5-bb74ff329d-1b8a2ae56e.zip"; hash = "sha512-G4oq5W7mClbp0gXNS2ByoVA8UGm467kFcQ+XT/AJig0HAGQcE34KjZje3xRCMVahBqlDNpXL9SV0gQ9VAA/cqw=="; };
|
||||||
|
"@protobufjs/eventemitter@npm:1.1.1" = { filename = "@protobufjs-eventemitter-npm-1.1.1-dbe0dfc812-8e06193d46.zip"; hash = "sha512-jgYZPUYpxefAnU+MLduo/E36c58BSfM6HZAVaNNbt7i1J3pOhFK6873QswL9WZzyVdGTJnqpOgpHR+I80HPErA=="; };
|
||||||
|
"@protobufjs/fetch@npm:1.1.1" = { filename = "@protobufjs-fetch-npm-1.1.1-b98d5396a8-a497ff5433.zip"; hash = "sha512-pJf/VDOFToV38EJ5g+o5uRE7SagSD5RRUpHXYzJwYdLDAT5g4k6kNtCR2vrgGg9usYZ+OxYWBF2Wox2LPGRu1A=="; };
|
||||||
|
"@protobufjs/float@npm:1.0.2" = { filename = "@protobufjs-float-npm-1.0.2-5678f64d08-18f2bdede7.zip"; hash = "sha512-GPK97edv/PAXBwivFcnJ22JZt3HmuExRsG3zSpwznbvuwmfRTOC93SCswUKx2YDZg9MUNDmN9/mOsMlKDreQaQ=="; };
|
||||||
|
"@protobufjs/path@npm:1.1.2" = { filename = "@protobufjs-path-npm-1.1.2-641d08de76-cece0a938e.zip"; hash = "sha512-zs4Kk45/Xf0voD+MFPLxz4sNbhOscyb/TJbqMR7/1ft64LunVPv1BTEq8uOFACUMkOaFBrl8AjYKQ3k9iKDYtA=="; };
|
||||||
|
"@protobufjs/pool@npm:1.1.0" = { filename = "@protobufjs-pool-npm-1.1.0-47a76f96a1-eda2718b7f.zip"; hash = "sha512-7aJxi38iKsbmrTb3WKku+Q0mUmAmoZ9PF/Zo9F4DBqW9c03vP0j1H4E0rgl4tiYqXFF8CLEVpVF1bRo6rfzwOA=="; };
|
||||||
|
"@protobufjs/utf8@npm:1.1.2" = { filename = "@protobufjs-utf8-npm-1.1.2-9c7ca5968f-975c6e2c03.zip"; hash = "sha512-l1xuLAMZvVDBdTHRhrU3qIy00yBePKkpfNhCtjHTQdoO4/8WvoZWzSZgdW3HU7KcimoUwK2O1gLB+R3fIuez8w=="; };
|
||||||
|
"@quixos/camino-datatypes@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=e061e290709c672d2dc675e5c871733de195211a" = { filename = "@quixos-camino-datatypes-https-faf980267e-4ed01c75c1.zip"; hash = "sha512-TtAcdcG7mReSI/kmbu+U91w39uYetoOiQbq6ex48zaTphDXn1vO1aaw0k8kEDhQgE1LMZrFgHyPzjlUIkyKdrA=="; };
|
||||||
"@types/node@npm:24.13.3" = { filename = "@types-node-npm-24.13.3-b512a0bbeb-a5bc08f49b.zip"; hash = "sha512-pbwI9JuVgdzcqQ4CzXcZejx5mECAdmSULlzVFhpVPU0q6AZPfjKUQQ10jX0Ji1wYSL5/pQwG8pxBk6sWvk5Z6w=="; };
|
"@types/node@npm:24.13.3" = { filename = "@types-node-npm-24.13.3-b512a0bbeb-a5bc08f49b.zip"; hash = "sha512-pbwI9JuVgdzcqQ4CzXcZejx5mECAdmSULlzVFhpVPU0q6AZPfjKUQQ10jX0Ji1wYSL5/pQwG8pxBk6sWvk5Z6w=="; };
|
||||||
|
"@types/node@npm:26.1.1" = { filename = "@types-node-npm-26.1.1-7a7a9f32f4-25ac509319.zip"; hash = "sha512-JaxQkxlXNt0HTVAnrbuoVhe8lR1aQVBuvTuQcwSKy3IzYT84ItX5uUR/nAhByR9jh8Rig5FnZ+PvefI5F0UsRg=="; };
|
||||||
"@typescript/vfs@npm:1.6.4" = { filename = "@typescript-vfs-npm-1.6.4-78935c9d3e-acb9de42f2.zip"; hash = "sha512-rLneQvI/2j9149eQC6EG7zI6L0588OSpTbtFfibTU8pjuzUZPtGjL8hzPzrlkJlhKykLBr1iIWlIYb65v7YvYg=="; };
|
"@typescript/vfs@npm:1.6.4" = { filename = "@typescript-vfs-npm-1.6.4-78935c9d3e-acb9de42f2.zip"; hash = "sha512-rLneQvI/2j9149eQC6EG7zI6L0588OSpTbtFfibTU8pjuzUZPtGjL8hzPzrlkJlhKykLBr1iIWlIYb65v7YvYg=="; };
|
||||||
"debug@npm:4.4.3" = { filename = "debug-npm-4.4.3-0105c6123a-d79136ec6c.zip"; hash = "sha512-15E27GyD7L79D2pVk9pqnJHsTX3cS1TIg9bnHsmsy19noaXpbQCjKBlrW1yG02XpjYo6cIVqrxa057GYXmf1pg=="; };
|
"debug@npm:4.4.3" = { filename = "debug-npm-4.4.3-0105c6123a-d79136ec6c.zip"; hash = "sha512-15E27GyD7L79D2pVk9pqnJHsTX3cS1TIg9bnHsmsy19noaXpbQCjKBlrW1yG02XpjYo6cIVqrxa057GYXmf1pg=="; };
|
||||||
|
"long@npm:5.3.2" = { filename = "long-npm-5.3.2-f80d0f7d39-7130fe1cbc.zip"; hash = "sha512-cTD+HLzi3KBnNLNbcNOAyj9wJxx/iFLJIqfGLIbE418MOSkFZeynEzxiWQjUDhJqxXwCsbGkY2uUV9d+HmC5gQ=="; };
|
||||||
"ms@npm:2.1.3" = { filename = "ms-npm-2.1.3-81ff3cfac1-d924b57e73.zip"; hash = "sha512-2SS1fnMSs7Y60h/Fs9wK9eeNYaH8fPtUV+2vJjJr9ivlMHzIf/toYu8cKzOwIzzbXU8BxMlYzA1mCUi2Wih6SA=="; };
|
"ms@npm:2.1.3" = { filename = "ms-npm-2.1.3-81ff3cfac1-d924b57e73.zip"; hash = "sha512-2SS1fnMSs7Y60h/Fs9wK9eeNYaH8fPtUV+2vJjJr9ivlMHzIf/toYu8cKzOwIzzbXU8BxMlYzA1mCUi2Wih6SA=="; };
|
||||||
|
"protobufjs@npm:7.6.5" = { filename = "protobufjs-npm-7.6.5-ff4a872120-863eaca9c6.zip"; hash = "sha512-hj6sqcb0W/z7h4fFRfU+nGaWUH4yjjmBtGQ8EtNd9/KCYCHBDj/TC+80LBOo6dMGVHusnAhJ7jv1D3cKEvAdxQ=="; };
|
||||||
"typescript@npm:5.4.5" = { filename = "typescript-npm-5.4.5-8568a42232-2954022ada.zip"; hash = "sha512-KVQCKto0D9PWqeK45TT2XVfJLV85iaJjdUp4q6VJ9+ZSmswZIZE1YKS4FsRtzn30pNKfnxGj3A1CE7t20EMlHg=="; };
|
"typescript@npm:5.4.5" = { filename = "typescript-npm-5.4.5-8568a42232-2954022ada.zip"; hash = "sha512-KVQCKto0D9PWqeK45TT2XVfJLV85iaJjdUp4q6VJ9+ZSmswZIZE1YKS4FsRtzn30pNKfnxGj3A1CE7t20EMlHg=="; };
|
||||||
"typescript@npm:5.9.3" = { filename = "typescript-npm-5.9.3-48715be868-6bd7552ce3.zip"; hash = "sha512-a9dVLOOfl+cR21qgSPb5mVtT8cUvfYZnwavcFwDGinajCPV5zTCc5rU2Rt606aG+fIE6k7qvCijM1TajAnDhxQ=="; };
|
"typescript@npm:5.9.3" = { filename = "typescript-npm-5.9.3-48715be868-6bd7552ce3.zip"; hash = "sha512-a9dVLOOfl+cR21qgSPb5mVtT8cUvfYZnwavcFwDGinajCPV5zTCc5rU2Rt606aG+fIE6k7qvCijM1TajAnDhxQ=="; };
|
||||||
"typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>::version=5.4.5&hash=5adc0c" = { filename = "typescript-patch-6e159bfddb-db2ad2a16c.zip"; hash = "sha512-2yrSoWyoKfUEJ+6x2hVeekXlmO7HsIbYtOi6ROWiNfdY5gbWgcZpkiMNP8O4mVhl5f0LIqLJVIbQsyAPgwcuyQ=="; };
|
"typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>::version=5.4.5&hash=5adc0c" = { filename = "typescript-patch-6e159bfddb-db2ad2a16c.zip"; hash = "sha512-2yrSoWyoKfUEJ+6x2hVeekXlmO7HsIbYtOi6ROWiNfdY5gbWgcZpkiMNP8O4mVhl5f0LIqLJVIbQsyAPgwcuyQ=="; };
|
||||||
"typescript@patch:typescript@npm%3A5.9.3#optional!builtin<compat/typescript>::version=5.9.3&hash=5786d5" = { filename = "typescript-patch-6fda4d02cf-ad09fdf7a7.zip"; hash = "sha512-rQn996dWgU3OZbxgwWV7QNREUTRoWO6iMOEPLpWiidkYO24y5cEelazAzMIUtPNiidytS/GIawrbhNcR0zakMA=="; };
|
"typescript@patch:typescript@npm%3A5.9.3#optional!builtin<compat/typescript>::version=5.9.3&hash=5786d5" = { filename = "typescript-patch-6fda4d02cf-ad09fdf7a7.zip"; hash = "sha512-rQn996dWgU3OZbxgwWV7QNREUTRoWO6iMOEPLpWiidkYO24y5cEelazAzMIUtPNiidytS/GIawrbhNcR0zakMA=="; };
|
||||||
"undici-types@npm:7.18.2" = { filename = "undici-types-npm-7.18.2-3e6d69d829-85a7918911.zip"; hash = "sha512-haeRiRE6I4lZ16ZHNo5PfFVZw6QE69uPxEiBRc6UJvzYIlKoRKMCeY38Djfm+xeP9IHtA7xMr2NMV1fZ70NSHQ=="; };
|
"undici-types@npm:7.18.2" = { filename = "undici-types-npm-7.18.2-3e6d69d829-85a7918911.zip"; hash = "sha512-haeRiRE6I4lZ16ZHNo5PfFVZw6QE69uPxEiBRc6UJvzYIlKoRKMCeY38Djfm+xeP9IHtA7xMr2NMV1fZ70NSHQ=="; };
|
||||||
|
"undici-types@npm:8.3.0" = { filename = "undici-types-npm-8.3.0-d34470de3e-c8aa7e2fbe.zip"; hash = "sha512-yKp+L76/zlGWVNr63A7OWb6IjSzK8YD7RJXah157U20kVjRcOEBpx+bz6cmrdDXwdJV9owbxQjQ+7ob/gEiFWg=="; };
|
||||||
};
|
};
|
||||||
|
|
||||||
in overriddenProject
|
in overriddenProject
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ __metadata:
|
|||||||
version: 9
|
version: 9
|
||||||
cacheKey: 10c0
|
cacheKey: 10c0
|
||||||
|
|
||||||
|
"@automerge/automerge@npm:^3.3.0":
|
||||||
|
version: 3.3.0
|
||||||
|
resolution: "@automerge/automerge@npm:3.3.0"
|
||||||
|
checksum: 10c0/74b3a8ef9e3cbf40760e898727ecdad5128e52fd2b053809454ae7b2b85b4ec647c3cba2a635cdbf831f2315731ad425da37a784b79bf13bc86ec28a7e92aff2
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@bufbuild/protobuf@npm:2.12.1, @bufbuild/protobuf@npm:^2.12.1":
|
"@bufbuild/protobuf@npm:2.12.1, @bufbuild/protobuf@npm:^2.12.1":
|
||||||
version: 2.12.1
|
version: 2.12.1
|
||||||
resolution: "@bufbuild/protobuf@npm:2.12.1"
|
resolution: "@bufbuild/protobuf@npm:2.12.1"
|
||||||
@@ -59,19 +66,107 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2":
|
||||||
|
version: 1.1.2
|
||||||
|
resolution: "@protobufjs/aspromise@npm:1.1.2"
|
||||||
|
checksum: 10c0/a83343a468ff5b5ec6bff36fd788a64c839e48a07ff9f4f813564f58caf44d011cd6504ed2147bf34835bd7a7dd2107052af755961c6b098fd8902b4f6500d0f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/base64@npm:^1.1.2":
|
||||||
|
version: 1.1.2
|
||||||
|
resolution: "@protobufjs/base64@npm:1.1.2"
|
||||||
|
checksum: 10c0/eec925e681081af190b8ee231f9bad3101e189abbc182ff279da6b531e7dbd2a56f1f306f37a80b1be9e00aa2d271690d08dcc5f326f71c9eed8546675c8caf6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/codegen@npm:^2.0.5":
|
||||||
|
version: 2.0.5
|
||||||
|
resolution: "@protobufjs/codegen@npm:2.0.5"
|
||||||
|
checksum: 10c0/1b8a2ae56ee60a56e9d205cd4b6072a1503c5069b8ebb905710f974ff0098a0d0700641c137e0a8d98dedf14423156a106a9433695cbf52574810f55000fdcab
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/eventemitter@npm:^1.1.1":
|
||||||
|
version: 1.1.1
|
||||||
|
resolution: "@protobufjs/eventemitter@npm:1.1.1"
|
||||||
|
checksum: 10c0/8e06193d4629c5e7c09d4f8c2ddba8fc4dfa739f0149f33a1d901568d35bb7b8b5277a4e8452baf3bdd0b302fd599cf255d193267aa93a0a4747e23cd073c4ac
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/fetch@npm:^1.1.1":
|
||||||
|
version: 1.1.1
|
||||||
|
resolution: "@protobufjs/fetch@npm:1.1.1"
|
||||||
|
dependencies:
|
||||||
|
"@protobufjs/aspromise": "npm:^1.1.1"
|
||||||
|
checksum: 10c0/a497ff5433854e8577f0427983ea39b9113b49a8120f94515291d763327061d2c3013e60e24ea436d091dafae01a0f6eb1867e3b1616045d96a31d8b3c646ed4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/float@npm:^1.0.2":
|
||||||
|
version: 1.0.2
|
||||||
|
resolution: "@protobufjs/float@npm:1.0.2"
|
||||||
|
checksum: 10c0/18f2bdede76ffcf0170708af15c9c9db6259b771e6b84c51b06df34a9c339dbbeec267d14ce0bddd20acc142b1d980d983d31434398df7f98eb0c94a0eb79069
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/path@npm:^1.1.2":
|
||||||
|
version: 1.1.2
|
||||||
|
resolution: "@protobufjs/path@npm:1.1.2"
|
||||||
|
checksum: 10c0/cece0a938e7f5dfd2fa03f8c14f2f1cf8b0d6e13ac7326ff4c96ea311effd5fb7ae0bba754fbf505312af2e38500250c90e68506b97c02360a43793d88a0d8b4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/pool@npm:^1.1.0":
|
||||||
|
version: 1.1.0
|
||||||
|
resolution: "@protobufjs/pool@npm:1.1.0"
|
||||||
|
checksum: 10c0/eda2718b7f222ac6e6ad36f758a92ef90d26526026a19f4f17f668f45e0306a5bd734def3f48f51f8134ae0978b6262a5c517c08b115a551756d1a3aadfcf038
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@protobufjs/utf8@npm:^1.1.1":
|
||||||
|
version: 1.1.2
|
||||||
|
resolution: "@protobufjs/utf8@npm:1.1.2"
|
||||||
|
checksum: 10c0/975c6e2c0319bd50c17531d186b537a88cb4d3205e3ca9297cd842b631d341da0ee3ff16be8656cd2660756dc753b29c8a6a14c0ad8ed602c1f91ddf22e7b3f3
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@quixos/camino-datatypes@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=e061e290709c672d2dc675e5c871733de195211a":
|
||||||
|
version: 0.1.0
|
||||||
|
resolution: "@quixos/camino-datatypes@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=e061e290709c672d2dc675e5c871733de195211a"
|
||||||
|
dependencies:
|
||||||
|
"@automerge/automerge": "npm:^3.3.0"
|
||||||
|
checksum: 10c0/4ed01c75c1bb99179223f9266eef94f75c37f6e61eb683a241baba7b1e3ccda4e98435e7d6f3b569ac3493c9040e14201352cc66b1601f23f38e550893229dac
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@quixos/camino-package-runtime@workspace:.":
|
"@quixos/camino-package-runtime@workspace:.":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@quixos/camino-package-runtime@workspace:."
|
resolution: "@quixos/camino-package-runtime@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@automerge/automerge": "npm:^3.3.0"
|
||||||
"@bufbuild/protobuf": "npm:^2.12.1"
|
"@bufbuild/protobuf": "npm:^2.12.1"
|
||||||
"@bufbuild/protoc-gen-es": "npm:^2.12.1"
|
"@bufbuild/protoc-gen-es": "npm:^2.12.1"
|
||||||
"@connectrpc/connect": "npm:^2.1.2"
|
"@connectrpc/connect": "npm:^2.1.2"
|
||||||
"@connectrpc/connect-node": "npm:^2.1.2"
|
"@connectrpc/connect-node": "npm:^2.1.2"
|
||||||
|
"@quixos/camino-datatypes": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=e061e290709c672d2dc675e5c871733de195211a"
|
||||||
"@types/node": "npm:^24"
|
"@types/node": "npm:^24"
|
||||||
|
protobufjs: "npm:^7.5.4"
|
||||||
typescript: "npm:^5.9.3"
|
typescript: "npm:^5.9.3"
|
||||||
|
bin:
|
||||||
|
camino-codegen-ts-runtime: dist/codegen-ts-runtime.js
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"@types/node@npm:>=13.7.0":
|
||||||
|
version: 26.1.1
|
||||||
|
resolution: "@types/node@npm:26.1.1"
|
||||||
|
dependencies:
|
||||||
|
undici-types: "npm:~8.3.0"
|
||||||
|
checksum: 10c0/25ac5093195736dd074d5027adbba85617bc951d5a41506ebd3b9073048acb7233613f3822d5f9b9447f9c0841c91f6387c46283916767e3ef79f23917452c46
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:^24":
|
"@types/node@npm:^24":
|
||||||
version: 24.13.3
|
version: 24.13.3
|
||||||
resolution: "@types/node@npm:24.13.3"
|
resolution: "@types/node@npm:24.13.3"
|
||||||
@@ -104,6 +199,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"long@npm:^5.3.2":
|
||||||
|
version: 5.3.2
|
||||||
|
resolution: "long@npm:5.3.2"
|
||||||
|
checksum: 10c0/7130fe1cbce2dca06734b35b70d380ca3f70271c7f8852c922a7c62c86c4e35f0c39290565eca7133c625908d40e126ac57c02b1b1a4636b9457d77e1e60b981
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ms@npm:^2.1.3":
|
"ms@npm:^2.1.3":
|
||||||
version: 2.1.3
|
version: 2.1.3
|
||||||
resolution: "ms@npm:2.1.3"
|
resolution: "ms@npm:2.1.3"
|
||||||
@@ -111,6 +213,25 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"protobufjs@npm:^7.5.4":
|
||||||
|
version: 7.6.5
|
||||||
|
resolution: "protobufjs@npm:7.6.5"
|
||||||
|
dependencies:
|
||||||
|
"@protobufjs/aspromise": "npm:^1.1.2"
|
||||||
|
"@protobufjs/base64": "npm:^1.1.2"
|
||||||
|
"@protobufjs/codegen": "npm:^2.0.5"
|
||||||
|
"@protobufjs/eventemitter": "npm:^1.1.1"
|
||||||
|
"@protobufjs/fetch": "npm:^1.1.1"
|
||||||
|
"@protobufjs/float": "npm:^1.0.2"
|
||||||
|
"@protobufjs/path": "npm:^1.1.2"
|
||||||
|
"@protobufjs/pool": "npm:^1.1.0"
|
||||||
|
"@protobufjs/utf8": "npm:^1.1.1"
|
||||||
|
"@types/node": "npm:>=13.7.0"
|
||||||
|
long: "npm:^5.3.2"
|
||||||
|
checksum: 10c0/863eaca9c6f45bfcfb8787c545f53e9c6696507e328e3981b4643c12d35df7f2826021c10e3fd30bef342c13a8e9d306547bac9c0849ee3bf50f770a12f01dc5
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"typescript@npm:5.4.5":
|
"typescript@npm:5.4.5":
|
||||||
version: 5.4.5
|
version: 5.4.5
|
||||||
resolution: "typescript@npm:5.4.5"
|
resolution: "typescript@npm:5.4.5"
|
||||||
@@ -157,3 +278,10 @@ __metadata:
|
|||||||
checksum: 10c0/85a79189113a238959d7a647368e4f7c5559c3a404ebdb8fc4488145ce9426fcd82252a844a302798dfc0e37e6fb178ff481ed03bc4caf634c5757d9ef43521d
|
checksum: 10c0/85a79189113a238959d7a647368e4f7c5559c3a404ebdb8fc4488145ce9426fcd82252a844a302798dfc0e37e6fb178ff481ed03bc4caf634c5757d9ef43521d
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"undici-types@npm:~8.3.0":
|
||||||
|
version: 8.3.0
|
||||||
|
resolution: "undici-types@npm:8.3.0"
|
||||||
|
checksum: 10c0/c8aa7e2fbebfce519654dafadc0ece59be888d2ccaf180fb4495da875e7b536d2456345c384069c7e6f3e9c9ab7435f074957da306f142343eee86ff8048855a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|||||||
Reference in New Issue
Block a user