diff --git a/.quixos-subtree-source.json b/.quixos-subtree-source.json index 1b91a5f..6966104 100644 --- a/.quixos-subtree-source.json +++ b/.quixos-subtree-source.json @@ -1,7 +1,7 @@ { "version": 1, "sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos", - "sourceCommit": "a9e94c961430415bcea302c47181d44ec7e2344c", + "sourceCommit": "29c36195235aeb4b60042e7ec4df0b0d2bcfff33", "sourcePath": "quixos-instance/packages/camino-package-runtime", "exportName": "camino-package-runtime", "mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git" diff --git a/dist/bindings.d.ts b/dist/bindings.d.ts index 466e41b..6f8dde2 100644 --- a/dist/bindings.d.ts +++ b/dist/bindings.d.ts @@ -10,7 +10,6 @@ export type MessageBinding = { decode(value: Value): T; }; export type QxLiveValue = ReturnType; -export declare const opaqueReactPropsBinding: MessageBinding>; export type BindingValue = B extends MessageBinding ? T : never; export type QxHandler = (context: C) => O | Promise; export type QxDerived = { @@ -71,6 +70,7 @@ export type QxPortSpec = { inputType: QxValueType; }; export type QxHandlerSpec = { + receiver?: "none"; inputType: QxValueType; outputType: QxValueType; eventType?: QxValueType; diff --git a/dist/bindings.d.ts.map b/dist/bindings.d.ts.map index 27a75e5..b6584da 100644 --- a/dist/bindings.d.ts.map +++ b/dist/bindings.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../src/bindings.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../src/bindings.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/bindings.js b/dist/bindings.js index a023e83..dce35f7 100644 --- a/dist/bindings.js +++ b/dist/bindings.js @@ -1,22 +1,7 @@ import { create } from "@bufbuild/protobuf"; import { ValueSchema, ObjectValueSchema } from "./camino/api_pb.js"; -import { derived, jsToProtoValue, liveValue, protoValueToJs } from "./index.js"; +import { derived, jsToProtoValue, liveValue, protoValueToJs, } from "./index.js"; import { assertReferenceFree, referenceToWire } from "./references.js"; -const reactPropsDescriptor = "org.quixos.web-studio.ReactProps"; -// Explicit temporary props exception, matching orch's RPC contract. Field shapes -// remain unchecked pending generics; ordinary messages and state do not gain it. -export const opaqueReactPropsBinding = { - encode(value) { - if (!value || typeof value !== "object" || Array.isArray(value)) - throw new Error("React props must be an object"); - return jsToProtoValue(value); - }, - decode(value) { - if (value.kind.case !== "objectValue") - throw new Error("React props must be an object"); - return protoValueToJs(value); - }, -}; export const qxDerived = (get) => ({ kind: "derived", get }); // Conversion belongs at the binding boundary. It does not add orchestrator validation. export const decodeQxValue = (type, value, messages) => { @@ -42,11 +27,9 @@ export const decodeQxValue = (type, value, messages) => { return value.kind.value.values.map((entry) => decodeQxValue(type.value, entry, messages)); } if (type.kind === "message") { - if (type.descriptorId !== reactPropsDescriptor) - assertReferenceFree(protoValueToJs(value)); + assertReferenceFree(protoValueToJs(value)); const decoded = requireMessage(messages, type.descriptorId).decode(value); - if (type.descriptorId !== reactPropsDescriptor) - assertReferenceFree(decoded); + assertReferenceFree(decoded); return decoded; } if (type.kind === "object-ref") { @@ -99,20 +82,19 @@ export const encodeQxValue = (type, value, messages) => { referenceToWire(value); return jsToProtoValue(value); } - if (type.kind !== "message" || type.descriptorId !== reactPropsDescriptor) - assertReferenceFree(value); + assertReferenceFree(value); if (type.kind === "message") { const encoded = requireMessage(messages, type.descriptorId).encode(value); - if (type.descriptorId !== reactPropsDescriptor) - assertReferenceFree(protoValueToJs(encoded)); + assertReferenceFree(protoValueToJs(encoded)); return encoded; } return jsToProtoValue(value); }; const inputValue = (context, type) => { if (type.kind === "message" || type.kind === "record") - return create(ValueSchema, { kind: { case: "objectValue", - value: create(ObjectValueSchema, { fields: context.inputProto }) } }); + return create(ValueSchema, { + kind: { case: "objectValue", value: create(ObjectValueSchema, { fields: context.inputProto }) }, + }); return context.inputProto.value; }; const inputFields = (type, value, messages) => { @@ -133,37 +115,86 @@ export const bindQxHandler = (spec, handler, messages) => { switch (port.kind) { case "state": { const state = raw.state(port.id); - return [name, { - ...(port.primitives.includes("read") ? { get: async () => decodeQxValue(port.valueType, (await state.live()).$quixosValue, messages), live: () => state.live() } : {}), - ...(port.primitives.includes("write") ? { set: async (value) => state.set(liveValue(encodeQxValue(port.valueType, value, messages))) } : {}), - }]; + return [ + name, + { + ...(port.primitives.includes("read") + ? { + get: async () => decodeQxValue(port.valueType, (await state.live()).$quixosValue, messages), + live: () => state.live(), + } + : {}), + ...(port.primitives.includes("write") + ? { + set: async (value) => state.set(liveValue(encodeQxValue(port.valueType, value, messages))), + } + : {}), + }, + ]; } case "edge": { const edge = raw.edge(port.id); - return [name, { ...Object.fromEntries(port.primitives.map((primitive) => [primitive, edge[primitive]])), + return [ + name, + { + ...Object.fromEntries(port.primitives.map((primitive) => [ + primitive, + edge[primitive], + ])), ...(port.primitives.includes("resolve") ? { collection: edge.collection } : {}), - ...(port.primitives.includes("resolve") && port.primitives.includes("connect") && port.primitives.includes("disconnect") ? { replace: edge.replace } : {}) }]; + ...(port.primitives.includes("resolve") && + port.primitives.includes("connect") && + port.primitives.includes("disconnect") + ? { replace: edge.replace } + : {}), + }, + ]; } case "interface": { const target = raw.interface(port.id); - return [name, { objectId: target.objectId, - live: Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [name, + return [ + name, + { + objectId: target.objectId, + live: Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [ + name, (input) => target.live(operation.id, inputFields(operation.inputType, input, messages)), ])), - ...Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [name, - async (input) => decodeQxValue(operation.outputType, (await target.live(operation.id, inputFields(operation.inputType, input, messages))).$quixosValue, messages), - ])) }]; + ...Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [ + name, + async (input) => decodeQxValue(operation.outputType, (await target.live(operation.id, inputFields(operation.inputType, input, messages))) + .$quixosValue, messages), + ])), + }, + ]; } - case "constructor": return [name, { construct: (input) => raw.constructor(port.id).construct(inputFields(port.inputType, input, messages)) }]; + case "constructor": + return [ + name, + { + construct: (input) => raw.constructor(port.id).construct(inputFields(port.inputType, input, messages)), + }, + ]; } })); - return { objectId: raw.objectId, signal: raw.signal, - ...(raw.openSession ? { openSession: async () => { - const session = await raw.openSession(); - return { id: session.id, close: () => session.close(), - run: (work) => session.run((next) => work(bindContext(next))) }; - } } : {}), - input: decodeQxValue(spec.inputType, inputValue(raw, spec.inputType), messages), ports }; + return { + ...(spec.receiver === "none" ? {} : { objectId: raw.objectId }), + signal: raw.signal, + ...(spec.receiver !== "none" && raw.openSession + ? { + openSession: async () => { + const session = await raw.openSession(); + return { + id: session.id, + close: () => session.close(), + run: (work) => session.run((next) => work(bindContext(next))), + }; + }, + } + : {}), + input: decodeQxValue(spec.inputType, inputValue(raw, spec.inputType), messages), + ports, + }; }; const execute = async (raw) => { const context = bindContext(raw); diff --git a/dist/index.d.ts b/dist/index.d.ts index cbd3426..3023fa0 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -2,7 +2,7 @@ import http from "node:http"; import { type QxObjectRef } from "./references.js"; export * from "./bindings.js"; export { relationshipMap, relationshipList, relationshipSet } from "./relationships.js"; -export { createMigrationContext, migrationObjectId, serveMigration, type MigrationContext, type MigrationInput, type MigrationOutput, type MigrationEdge } from "./migration.js"; +export { createMigrationContext, migrationObjectId, serveMigration, type MigrationContext, type MigrationInput, type MigrationOutput, type MigrationEdge, } from "./migration.js"; import { type Client, type ConnectRouter } from "@connectrpc/connect"; import { CaminoService, type Value } from "./camino/api_pb.js"; import { OrchestratorRuntime } from "./quixos/orch_pb.js"; diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map index 7768e9d..ce7a0ed 100644 --- a/dist/index.d.ts.map +++ b/dist/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAA8E,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/H,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAC,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAGtF,OAAO,EAAC,sBAAsB,EAAE,iBAAiB,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAE/K,OAAO,EAAoC,KAAK,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAExG,OAAO,EACL,aAAa,EAOb,KAAK,KAAK,EACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAU1D,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AA+BnF,eAAO,MAAM,SAAS,cAAe,WAAW,wBAAsD,CAAC;AACvG,eAAO,MAAM,SAAS,UAAW,KAAK;IAAQ,YAAY;CAAU,CAAC;AAErE,eAAO,MAAM,cAAc,UAAW,OAAO,KAAG,KAiC/C,CAAC;AAEF,eAAO,MAAM,cAAc,UAAW,KAAK,GAAG,SAAS,KAAG,OAoBzD,CAAC;AAEF,eAAO,MAAM,eAAe,WAAY,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;;CACmC,CAAC;AAEjG,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,OAAO,IAAI;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;IAClB,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;IAC9C,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAClC,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,UAAU,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC9C,OAAO,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CAClG,CAAC;AACF,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;CAAC,CAAC;AACnI,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAA;CAAC,CAAC;AAC9H,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,WAAW,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;CACnG,CAAC;AACF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAClE,CAAC;AACF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,GAAG,eAAe,CAAC;AAEjF,MAAM,MAAM,cAAc,GAAG;IAC3B,2FAA2F;IAC3F,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5C,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAClC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACxC,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC;IACzC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClE,gFAAgF;IAChF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AACF,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,YAAY,OAAO,EAAE,MAAM,EAAyH;CACrJ;AAOD,eAAO,MAAM,oBAAoB,WACvB,YAAY,QACd,UAAU,WACP,cAAc,KACtB,cA8IF,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AACrF,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,cAAc,CAAA;CAAE,CAAC;AACtE,eAAO,MAAM,OAAO,QAAS,cAAc,KAAG,cAA4C,CAAC;AAyB3F,eAAO,MAAM,0BAA0B,WAAY;IACjD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAAC,CAAC;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,cA8FiB,aAAa,kBA2L9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,WAAY;IAC1C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAAC,CAAC;CAC1D,yEAaA,CAAC;AACF,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7B,YAAY,EAAE,OAAO,qBAAqB,EAAE,kBAAkB,EAAE,CAAC;CAClE,CAAC"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 248ec48..ef93a59 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,12 +1,12 @@ import http from "node:http"; import { readFileSync } from "node:fs"; import { createInvocationRegistry } from "./invocations.js"; -import { isObjectReference, referenceFromWire, referenceToWire, assertReferenceFree } from "./references.js"; +import { isObjectReference, referenceFromWire, referenceToWire, assertReferenceFree, } from "./references.js"; export * from "./bindings.js"; export { relationshipMap, relationshipList, relationshipSet } from "./relationships.js"; import { AsyncLocalStorage } from "node:async_hooks"; import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto"; -export { createMigrationContext, migrationObjectId, serveMigration } from "./migration.js"; +export { createMigrationContext, migrationObjectId, serveMigration, } from "./migration.js"; import { create, equals } from "@bufbuild/protobuf"; import { Code, ConnectError, createClient } from "@connectrpc/connect"; import { connectNodeAdapter, createConnectTransport } from "@connectrpc/connect-node"; @@ -27,13 +27,20 @@ const recordDependency = async (dependency) => { const bytesToBase64 = (value) => Buffer.from(value).toString("base64"); const base64ToBytes = (value) => Buffer.from(value, "base64"); const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value); -const isWrappedValue = (value) => isRecord(value) && "$quixosValue" in value && - isRecord(value.$quixosValue) && value.$quixosValue.$typeName === "camino.Value"; -export const objectRef = (reference) => { referenceToWire(reference); return reference; }; +const isWrappedValue = (value) => isRecord(value) && + "$quixosValue" in value && + isRecord(value.$quixosValue) && + value.$quixosValue.$typeName === "camino.Value"; +export const objectRef = (reference) => { + referenceToWire(reference); + return reference; +}; export const liveValue = (value) => ({ $quixosValue: value }); export const jsToProtoValue = (value) => { if (isObjectReference(value)) - return create(ValueSchema, { kind: { case: "refValue", value: create(RefValueSchema, { objectId: referenceToWire(value) }) } }); + return create(ValueSchema, { + kind: { case: "refValue", value: create(RefValueSchema, { objectId: referenceToWire(value) }) }, + }); if (isWrappedValue(value)) return value.$quixosValue; if (value === null || value === undefined) { @@ -56,49 +63,65 @@ export const jsToProtoValue = (value) => { } if (isRecord(value) && "$quixosRef" in value) throw new Error("Raw ID wrappers are not object references"); - if (isRecord(value) && typeof value.$quixosCrdtType === "string" && - typeof value.$quixosCrdtPayload === "string") { + if (isRecord(value) && typeof value.$quixosCrdtType === "string" && typeof value.$quixosCrdtPayload === "string") { return create(ValueSchema, { - kind: { case: "crdtValue", value: create(CrdtValueSchema, { + kind: { + case: "crdtValue", + value: create(CrdtValueSchema, { type: value.$quixosCrdtType, encoding: typeof value.$quixosCrdtEncoding === "string" ? value.$quixosCrdtEncoding : "base64", payload: base64ToBytes(value.$quixosCrdtPayload), - }) }, + }), + }, }); } if (!isRecord(value)) throw new Error(`Unsupported runtime value ${typeof value}`); return create(ValueSchema, { - kind: { case: "objectValue", value: create(ObjectValueSchema, { + kind: { + case: "objectValue", + value: create(ObjectValueSchema, { fields: Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, jsToProtoValue(entry)])), - }) }, + }), + }, }); }; export const protoValueToJs = (value) => { switch (value?.kind.case) { case "nullValue": - case undefined: return null; + case undefined: + return null; case "boolValue": case "numberValue": case "stringValue": - case "integerValue": return value.kind.value; - case "bytesValue": return bytesToBase64(value.kind.value); - case "refValue": return referenceFromWire(value.kind.value.objectId); - case "listValue": return value.kind.value.values.map(protoValueToJs); - case "objectValue": return Object.fromEntries(Object.entries(value.kind.value.fields).map(([key, entry]) => [key, protoValueToJs(entry)])); - case "crdtValue": return { - $quixosCrdtType: value.kind.value.type, - $quixosCrdtEncoding: value.kind.value.encoding, - $quixosCrdtPayload: bytesToBase64(value.kind.value.payload), - }; + case "integerValue": + return value.kind.value; + case "bytesValue": + return bytesToBase64(value.kind.value); + case "refValue": + return referenceFromWire(value.kind.value.objectId); + case "listValue": + return value.kind.value.values.map(protoValueToJs); + case "objectValue": + return Object.fromEntries(Object.entries(value.kind.value.fields).map(([key, entry]) => [key, protoValueToJs(entry)])); + case "crdtValue": + return { + $quixosCrdtType: value.kind.value.type, + $quixosCrdtEncoding: value.kind.value.encoding, + $quixosCrdtPayload: bytesToBase64(value.kind.value.payload), + }; } }; export const protoFieldsToJs = (fields) => Object.fromEntries(Object.entries(fields).map(([key, value]) => [key, protoValueToJs(value)])); export class RuntimeAuthorityError extends Error { retryable; - constructor(message) { super(message); this.name = "RuntimeAuthorityError"; this.retryable = /WORKSPACE_FENCED|STALE_EPOCH/.test(message); } + constructor(message) { + super(message); + this.name = "RuntimeAuthorityError"; + this.retryable = /WORKSPACE_FENCED|STALE_EPOCH/.test(message); + } } -const targetForEdge = (edge, projectionId) => edge.firstProjectionId === projectionId ? edge.secondObjectId : edge.firstObjectId; +const targetForEdge = (edge, projectionId) => (edge.firstProjectionId === projectionId ? edge.secondObjectId : edge.firstObjectId); export const createRuntimeContext = (camino, orch, request) => { const ports = new Map(); for (const dependency of request.dependencies) { @@ -130,25 +153,55 @@ export const createRuntimeContext = (camino, orch, request) => { case "edge": { const { edgeTypeId, projectionId } = dependency.binding.value; const dependencyObjectId = dependency.objectId || request.objectId; - const collectionResult = (response) => ({ revision: response.revision, - entries: response.entries.map((entry) => ({ edgeId: entry.edgeId, target: referenceFromWire(entry.targetObjectId), - ...(entry.key ? { key: entry.key.kind.case === "integerValue" ? BigInt(entry.key.kind.value) : protoValueToJs(entry.key) } : {}) })) }); + const collectionResult = (response) => ({ + revision: response.revision, + entries: response.entries.map((entry) => ({ + edgeId: entry.edgeId, + target: referenceFromWire(entry.targetObjectId), + ...(entry.key + ? { + key: entry.key.kind.case === "integerValue" + ? BigInt(entry.key.kind.value) + : protoValueToJs(entry.key), + } + : {}), + })), + }); const edge = { edgeTypeId, projectionId, async collection() { - await recordDependency({ kind: "edge", objectId: dependencyObjectId, attachmentId: edgeTypeId, projectionId }); + await recordDependency({ + kind: "edge", + objectId: dependencyObjectId, + attachmentId: edgeTypeId, + projectionId, + }); return collectionResult(await camino.readCollection({ objectId: dependencyObjectId, edgeTypeId, projectionId })); }, async replace(entries, expectedRevision) { - return collectionResult(await camino.replaceCollection({ objectId: dependencyObjectId, edgeTypeId, projectionId, expectedRevision, + return collectionResult(await camino.replaceCollection({ + objectId: dependencyObjectId, + edgeTypeId, + projectionId, + expectedRevision, entries: entries.map((entry) => { assertReferenceFree(entry.key); - return { edgeId: entry.edgeId ?? "", targetObjectId: referenceToWire(entry.target), key: entry.key === undefined ? undefined : jsToProtoValue(entry.key) }; - }) })); + return { + edgeId: entry.edgeId ?? "", + targetObjectId: referenceToWire(entry.target), + key: entry.key === undefined ? undefined : jsToProtoValue(entry.key), + }; + }), + })); }, async resolve() { - await recordDependency({ kind: "edge", objectId: dependencyObjectId, attachmentId: edgeTypeId, projectionId }); + await recordDependency({ + kind: "edge", + objectId: dependencyObjectId, + attachmentId: edgeTypeId, + projectionId, + }); const result = await camino.resolveEdge({ objectId: dependencyObjectId, edgeTypeId, projectionId }); return result.edges.map((entry) => referenceFromWire(targetForEdge(entry, projectionId))); }, @@ -239,7 +292,9 @@ export const createRuntimeContext = (camino, orch, request) => { return port; }; return { - objectId: referenceFromWire(request.objectId), + get objectId() { + return referenceFromWire(request.objectId); + }, input: protoFieldsToJs(request.input), inputProto: request.input, ports, @@ -265,8 +320,10 @@ const protoDependencies = (dependencies) => dependencies.map((entry) => create(D export const createPackageRuntimeRoutes = (config) => { const invocations = createInvocationRegistry(); const headers = {}; - const processToken = process.env.CAMINO_RUNTIME_AUTH_TOKEN ?? (process.env.CAMINO_RUNTIME_AUTH_TOKEN_FILE - ? readFileSync(process.env.CAMINO_RUNTIME_AUTH_TOKEN_FILE, "utf8").trim() : ""); + const processToken = process.env.CAMINO_RUNTIME_AUTH_TOKEN ?? + (process.env.CAMINO_RUNTIME_AUTH_TOKEN_FILE + ? readFileSync(process.env.CAMINO_RUNTIME_AUTH_TOKEN_FILE, "utf8").trim() + : ""); if (processToken) { headers["x-camino-runtime-token"] = processToken; } @@ -276,12 +333,14 @@ export const createPackageRuntimeRoutes = (config) => { const camino = createClient(CaminoService, createConnectTransport({ baseUrl: config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310", httpVersion: "1.1", - interceptors: headers["x-camino-runtime-token"] ? [ - (next) => async (request) => { - request.header.set("x-camino-runtime-token", headers["x-camino-runtime-token"]); - return await next(request); - }, - ] : [], + interceptors: headers["x-camino-runtime-token"] + ? [ + (next) => async (request) => { + request.header.set("x-camino-runtime-token", headers["x-camino-runtime-token"]); + return await next(request); + }, + ] + : [], })); const orch = createClient(OrchestratorRuntime, createConnectTransport({ baseUrl: config.orchUrl ?? process.env.QUIXOS_ORCH_URL ?? "http://127.0.0.1:7311", @@ -298,16 +357,23 @@ export const createPackageRuntimeRoutes = (config) => { }; const clientsFor = (request) => { const context = request.context; - if (process.env.QUIXOS_RUNTIME_INSTANCE_ID && (!context?.grant || context.instanceId !== process.env.QUIXOS_RUNTIME_INSTANCE_ID || !context.workspaceEpoch)) { + if (process.env.QUIXOS_RUNTIME_INSTANCE_ID && + (!context?.grant || context.instanceId !== process.env.QUIXOS_RUNTIME_INSTANCE_ID || !context.workspaceEpoch)) { throw new ConnectError("Managed invocation requires an exact instance and epoch grant", Code.Unauthenticated); } if (!context?.grant) return { camino, orch }; - const transport = (url) => createConnectTransport({ baseUrl: url, httpVersion: "1.1", interceptors: [(next) => async (call) => { + const transport = (url) => createConnectTransport({ + baseUrl: url, + httpVersion: "1.1", + interceptors: [ + (next) => async (call) => { call.header.set("x-quixos-invocation-grant", context.grant); call.header.set("x-camino-runtime-token", processToken); return next(call); - }] }); + }, + ], + }); return { camino: createClient(CaminoService, transport(config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310")), orch: createClient(OrchestratorRuntime, transport(config.orchUrl ?? process.env.QUIXOS_ORCH_URL ?? "http://127.0.0.1:7311")), @@ -315,9 +381,12 @@ export const createPackageRuntimeRoutes = (config) => { }; const runtimeControl = async (operation, input) => { const response = await fetch(`${config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310"}/__runtime/${operation}`, { - method: "POST", headers: { "content-type": "application/json", "x-camino-runtime-token": processToken }, body: JSON.stringify(input), signal: AbortSignal.timeout(10_000), + method: "POST", + headers: { "content-type": "application/json", "x-camino-runtime-token": processToken }, + body: JSON.stringify(input), + signal: AbortSignal.timeout(10_000), }); - const value = await response.json(); + const value = (await response.json()); if (!response.ok) throw new RuntimeAuthorityError(value.error ?? "Runtime authority request failed"); return value; @@ -327,8 +396,13 @@ export const createPackageRuntimeRoutes = (config) => { return; runtimeContext.openSession = async () => { const ownerId = request.context.ownerConformanceId; - const registration = { grant: request.context.grant, objectId: request.objectId, ownerId, - sessionId: `session:${randomBytes(16).toString("hex")}`, token: randomBytes(32).toString("base64url") }; + const registration = { + grant: request.context.grant, + objectId: request.objectId, + ownerId, + sessionId: `session:${randomBytes(16).toString("hex")}`, + token: randomBytes(32).toString("base64url"), + }; const register = () => runtimeControl("register-session", registration); const registered = await register().catch((error) => { // Retry a transport/lost-response failure with exactly the same identity. @@ -347,7 +421,10 @@ export const createPackageRuntimeRoutes = (config) => { // by the caller without replaying a side-effecting callback. const grant = await runtimeControl("acquire-session", registered); const execution = invocations.begin(grant.invocationId); - const sessionRequest = { ...request, context: { grant: grant.grant, instanceId: grant.instanceId, workspaceEpoch: grant.epoch } }; + const sessionRequest = { + ...request, + context: { grant: grant.grant, instanceId: grant.instanceId, workspaceEpoch: grant.epoch }, + }; const clients = clientsFor(sessionRequest); const context = createRuntimeContext(clients.camino, clients.orch, sessionRequest); context.signal = execution.signal; @@ -359,7 +436,10 @@ export const createPackageRuntimeRoutes = (config) => { await runtimeControl("complete-invocation", { invocationId: grant.invocationId }).catch((error) => console.error("Session completion will be reconciled by the host", error)); } }, - async close() { await runtimeControl("close-session", registered); closed = true; }, + async close() { + await runtimeControl("close-session", registered); + closed = true; + }, }; }; }; @@ -370,9 +450,15 @@ export const createPackageRuntimeRoutes = (config) => { exportIds: Object.keys(config.exports), capabilities: ["invocation-completion-v1", "instance-authentication-v1", "epoch-grants-v1"], instanceId: process.env.QUIXOS_RUNTIME_INSTANCE_ID ?? "", - authenticationProof: request.nonce && processToken ? createHmac("sha256", processToken) - .update(JSON.stringify([request.nonce, process.env.QUIXOS_RUNTIME_INSTANCE_ID ?? "", config.packageRevisionId])) - .digest("hex") : "", + authenticationProof: request.nonce && processToken + ? createHmac("sha256", processToken) + .update(JSON.stringify([ + request.nonce, + process.env.QUIXOS_RUNTIME_INSTANCE_ID ?? "", + config.packageRevisionId, + ])) + .digest("hex") + : "", }), getInvocationStatus: (request, context) => { authenticateInstance(context.requestHeader); @@ -437,7 +523,12 @@ export const createPackageRuntimeRoutes = (config) => { return await pending; const establish = (async () => { const controller = new AbortController(); - const stream = camino.watchObject({ objectId: dependency.objectId, includeSnapshot: true, attachmentIds: request.context?.grant ? [dependency.attachmentId] : [] }, { signal: controller.signal })[Symbol.asyncIterator](); + const stream = camino + .watchObject({ + objectId: dependency.objectId, + includeSnapshot: true, + attachmentIds: request.context?.grant ? [dependency.attachmentId] : [], + }, { signal: controller.signal })[Symbol.asyncIterator](); try { // Camino subscribes before producing the snapshot, so once this // resolves the following state/edge read cannot race the stream. @@ -507,10 +598,7 @@ export const createPackageRuntimeRoutes = (config) => { await abort; break; } - const outcome = await Promise.race([ - ...[...subscriptions.values()].map((entry) => entry.next), - abort, - ]); + const outcome = await Promise.race([...[...subscriptions.values()].map((entry) => entry.next), abort]); if (outcome === "abort") break; const subscription = subscriptions.get(outcome.key); diff --git a/dist/invocations.d.ts.map b/dist/invocations.d.ts.map index e8eaf0d..6a35a0f 100644 --- a/dist/invocations.d.ts.map +++ b/dist/invocations.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"invocations.d.ts","sourceRoot":"","sources":["../src/invocations.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB;IAGjC,KAAK,KAAK,MAAM;QAOL,MAAM;QAA2B,MAAM;;IAElD,MAAM,KAAK,MAAM;QAAa,YAAY;QAAM,KAAK;;IACrD,MAAM,KAAK,MAAM;;;;CASpB,CAAC"} \ No newline at end of file +{"version":3,"file":"invocations.d.ts","sourceRoot":"","sources":["../src/invocations.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB;IAGjC,KAAK,KAAK,MAAM;QASZ,MAAM;QACN,MAAM;;IAKV,MAAM,KAAK,MAAM;QACN,YAAY;QAAM,KAAK;;IAElC,MAAM,KAAK,MAAM;;;;CASpB,CAAC"} \ No newline at end of file diff --git a/dist/invocations.js b/dist/invocations.js index 25280b9..7013233 100644 --- a/dist/invocations.js +++ b/dist/invocations.js @@ -11,9 +11,16 @@ export const createInvocationRegistry = () => { throw new Error("Invocation registry full; explicit runtime retirement required"); const entry = { state: "running", controller: new AbortController() }; entries.set(id, entry); - return { signal: entry.controller.signal, finish(failed = false) { entry.state = failed ? "failed" : "completed"; } }; + return { + signal: entry.controller.signal, + finish(failed = false) { + entry.state = failed ? "failed" : "completed"; + }, + }; + }, + status(id) { + return { invocationId: id, state: entries.get(id)?.state ?? "unknown" }; }, - status(id) { return { invocationId: id, state: entries.get(id)?.state ?? "unknown" }; }, cancel(id) { const entry = entries.get(id); if (entry && ["running", "cancellation-requested"].includes(entry.state)) { diff --git a/dist/migration.d.ts.map b/dist/migration.d.ts.map index e003bdd..0d94e01 100644 --- a/dist/migration.d.ts.map +++ b/dist/migration.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IACxD,KAAK,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;QAAC,MAAM,EAAE,CAAC,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAC5H,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAC;QAChD,MAAM,CAAC,EAAE;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAC,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,aAAa,EAAE,CAAA;KAAC,EAAE,CAAC;CAC7E,CAAC;AACF,MAAM,MAAM,aAAa,GAAG;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAC,CAAC;AACjQ,MAAM,MAAM,eAAe,GAAG;IAAC,aAAa,EAAE,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IACnE,MAAM,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAC,EAAE,CAAC;IAC3D,OAAO,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IAChE,gBAAgB,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,EAAE,CAAA;KAAC,EAAE,CAAA;CAAC,CAAC;AAC9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAC,EAAE,CAAC;IAC9D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IACjD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAAC;IACrC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;CAC1D,CAAC;AACF,eAAO,MAAM,iBAAiB,gBAAiB,MAAM,QAAQ,MAAM,cAAc,MAAM,WACwB,CAAC;AAEhH;sEACsE;AACtE,eAAO,MAAM,sBAAsB,UAAW,cAAc;;;CAgD3D,CAAC;AAEF;;+EAE+E;AAC/E,eAAO,MAAM,cAAc,YAAmB,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,kBAehH,CAAC"} \ No newline at end of file +{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,CAAC,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;QACpB,MAAM,EAAE,CAAC,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,MAAM,CAAC,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAE,EAAE,CAAC;QAChD,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;KACzB,EAAE,CAAC;CACL,CAAC;AACF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AACF,MAAM,MAAM,eAAe,GAAG;IAC5B,aAAa,EAAE,CAAC,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC7D,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClE,gBAAgB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,EAAE,CAAA;KAAE,EAAE,CAAC;CAC9D,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAChE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IACjD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAAC;IACrC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;CAC1D,CAAC;AACF,eAAO,MAAM,iBAAiB,gBAAiB,MAAM,QAAQ,MAAM,cAAc,MAAM,WAGnE,CAAC;AAErB;sEACsE;AACtE,eAAO,MAAM,sBAAsB,UAAW,cAAc;;;CAgF3D,CAAC;AAEF;;+EAE+E;AAC/E,eAAO,MAAM,cAAc,YAAmB,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,kBAgBhH,CAAC"} \ No newline at end of file diff --git a/dist/migration.js b/dist/migration.js index 5bf1173..db59b78 100644 --- a/dist/migration.js +++ b/dist/migration.js @@ -1,11 +1,21 @@ import { createHash } from "node:crypto"; -export const migrationObjectId = (executionId, port, logicalKey) => `obj:migration:${createHash("sha256").update(JSON.stringify([executionId, port, logicalKey])).digest("hex")}`; +export const migrationObjectId = (executionId, port, logicalKey) => `obj:migration:${createHash("sha256") + .update(JSON.stringify([executionId, port, logicalKey])) + .digest("hex")}`; /** No ordinary RuntimeContext or network/database clients are supplied here. * Process isolation belongs to the host, not this convenience API. */ export const createMigrationContext = (input) => { - if (input.schemaVersion !== 1 || !input.executionId || new Set(input.ports.map((entry) => entry.name)).size !== input.ports.length) + if (input.schemaVersion !== 1 || + !input.executionId || + new Set(input.ports.map((entry) => entry.name)).size !== input.ports.length) throw new Error("Invalid migration input"); - const output = { schemaVersion: 1, executionId: input.executionId, writes: [], creates: [], edgeReplacements: [] }; + const output = { + schemaVersion: 1, + executionId: input.executionId, + writes: [], + creates: [], + edgeReplacements: [], + }; const port = (name, access) => { const selected = input.ports.find((entry) => entry.name === name); if (!selected?.access.includes(access) || (selected.view === "old" && access !== "read")) @@ -17,7 +27,8 @@ export const createMigrationContext = (input) => { const selected = port(name, "read"), states = structuredClone(selected.states ?? []); if (selected.view === "new" && Object.hasOwn(selected, "defaultValue")) for (const helper of output.creates) { - if (input.ports.find((entry) => entry.name === helper.port)?.atomId === selected.attachedAtomId && !states.some((entry) => entry.objectId === helper.objectId)) + if (input.ports.find((entry) => entry.name === helper.port)?.atomId === selected.attachedAtomId && + !states.some((entry) => entry.objectId === helper.objectId)) states.push({ objectId: helper.objectId, value: structuredClone(selected.defaultValue) }); } if (selected.view === "new") @@ -30,9 +41,11 @@ export const createMigrationContext = (input) => { else states[existing] = entry; } - return states.sort((a, b) => a.objectId < b.objectId ? -1 : a.objectId > b.objectId ? 1 : 0); + return states.sort((a, b) => (a.objectId < b.objectId ? -1 : a.objectId > b.objectId ? 1 : 0)); + }, + read(name, objectId) { + return context.enumerate(name).find((entry) => entry.objectId === objectId)?.value; }, - read(name, objectId) { return context.enumerate(name).find((entry) => entry.objectId === objectId)?.value; }, write(name, objectId, value) { port(name, "write"); const previous = output.writes.findIndex((entry) => entry.port === name && entry.objectId === objectId); @@ -53,7 +66,9 @@ export const createMigrationContext = (input) => { }, edges(name) { const selected = port(name, "read"); - const replacement = selected.view === "new" ? output.edgeReplacements.find((entry) => input.ports.find((candidate) => candidate.name === entry.port)?.binding === selected.binding) : undefined; + const replacement = selected.view === "new" + ? output.edgeReplacements.find((entry) => input.ports.find((candidate) => candidate.name === entry.port)?.binding === selected.binding) + : undefined; return structuredClone(replacement?.edges ?? selected.edges ?? []); }, replaceEdges(name, edges) { diff --git a/dist/quixos/orch_pb.d.ts b/dist/quixos/orch_pb.d.ts index 57a7953..fc49267 100644 --- a/dist/quixos/orch_pb.d.ts +++ b/dist/quixos/orch_pb.d.ts @@ -1,5 +1,5 @@ import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; -import type { CaminoObject, Value } from "../camino/api_pb.js"; +import type { CaminoObject, CrdtValue, Value } from "../camino/api_pb.js"; import type { PackageDescriptor } from "./package_pb.js"; import type { CapabilityRef, PackageExportRef } from "./refs_pb.js"; import type { DerivedDependency } from "./runtime_pb.js"; @@ -113,6 +113,30 @@ export type InvokeCapabilityRequest = Message<"quixos.orch.InvokeCapabilityReque * Use `create(InvokeCapabilityRequestSchema)` to create a new message. */ export declare const InvokeCapabilityRequestSchema: GenMessage; +/** + * @generated from message quixos.orch.InvokeClassCapabilityRequest + */ +export type InvokeClassCapabilityRequest = Message<"quixos.orch.InvokeClassCapabilityRequest"> & { + /** + * @generated from field: string conformance_id = 1; + */ + conformanceId: string; + /** + * @generated from field: string operation_id = 2; + */ + operationId: string; + /** + * @generated from field: map input = 3; + */ + input: { + [key: string]: Value; + }; +}; +/** + * Describes the message quixos.orch.InvokeClassCapabilityRequest. + * Use `create(InvokeClassCapabilityRequestSchema)` to create a new message. + */ +export declare const InvokeClassCapabilityRequestSchema: GenMessage; /** * @generated from message quixos.orch.InvokeCapabilityResponse */ @@ -141,12 +165,80 @@ export type InvokeCapabilityResponse = Message<"quixos.orch.InvokeCapabilityResp * @generated from field: repeated quixos.runtime.DerivedDependency dependencies = 6; */ dependencies: DerivedDependency[]; + /** + * @generated from field: quixos.orch.FieldEditing field_editing = 7; + */ + fieldEditing?: FieldEditing | undefined; }; /** * Describes the message quixos.orch.InvokeCapabilityResponse. * Use `create(InvokeCapabilityResponseSchema)` to create a new message. */ export declare const InvokeCapabilityResponseSchema: GenMessage; +/** + * Resolved from the checked native getter/setter binding, not Value.source. + * + * @generated from message quixos.orch.FieldEditing + */ +export type FieldEditing = Message<"quixos.orch.FieldEditing"> & { + /** + * @generated from field: string getter_operation_id = 1; + */ + getterOperationId: string; + /** + * @generated from field: string setter_operation_id = 2; + */ + setterOperationId: string; + /** + * @generated from field: string document_type = 3; + */ + documentType: string; + /** + * @generated from field: string binding_digest = 4; + */ + bindingDigest: string; +}; +/** + * Describes the message quixos.orch.FieldEditing. + * Use `create(FieldEditingSchema)` to create a new message. + */ +export declare const FieldEditingSchema: GenMessage; +/** + * @generated from message quixos.orch.EditCapabilityFieldRequest + */ +export type EditCapabilityFieldRequest = Message<"quixos.orch.EditCapabilityFieldRequest"> & { + /** + * The public getter; setter must belong to the same value member. + * + * @generated from field: quixos.CapabilityRef capability = 1; + */ + capability?: CapabilityRef | undefined; + /** + * @generated from field: string object_id = 2; + */ + objectId: string; + /** + * @generated from field: string setter_operation_id = 3; + */ + setterOperationId: string; + /** + * @generated from field: string binding_digest = 4; + */ + bindingDigest: string; + /** + * @generated from field: camino.CrdtValue update = 5; + */ + update?: CrdtValue | undefined; + /** + * @generated from field: string client_mutation_id = 6; + */ + clientMutationId: string; +}; +/** + * Describes the message quixos.orch.EditCapabilityFieldRequest. + * Use `create(EditCapabilityFieldRequestSchema)` to create a new message. + */ +export declare const EditCapabilityFieldRequestSchema: GenMessage; /** * @generated from message quixos.orch.WatchCapabilityRequest */ @@ -203,6 +295,10 @@ export type WatchCapabilityEvent = Message<"quixos.orch.WatchCapabilityEvent"> & * @generated from field: bool initial = 7; */ initial: boolean; + /** + * @generated from field: quixos.orch.FieldEditing field_editing = 8; + */ + fieldEditing?: FieldEditing | undefined; }; /** * Describes the message quixos.orch.WatchCapabilityEvent. @@ -212,7 +308,14 @@ export declare const WatchCapabilityEventSchema: GenMessage & {}; +export type GetWorkspaceRequest = Message<"quixos.orch.GetWorkspaceRequest"> & { + /** + * Revision polling must not download the entire interface graph. + * + * @generated from field: bool include_interface_contracts = 1; + */ + includeInterfaceContracts: boolean; +}; /** * Describes the message quixos.orch.GetWorkspaceRequest. * Use `create(GetWorkspaceRequestSchema)` to create a new message. @@ -235,8 +338,8 @@ export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & */ sourceRootCommit: string; /** - * Checked constructors whose wire input can be empty. Web Studio intersects - * this with its temporary Createable marker; the marker is not a factory. + * Checked constructors whose wire input can be empty. The create panel uses + * class factory conformances instead of this constructor inventory. * * @generated from field: repeated string empty_input_constructible_atom_ids = 4; */ @@ -249,12 +352,60 @@ export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & * @generated from field: repeated quixos.orch.ConstructorInputContract constructor_inputs = 6; */ constructorInputs: ConstructorInputContract[]; + /** + * Exact closed interface contracts used by checked presentation consumers. + * + * @generated from field: string interfaces_json = 7; + */ + interfacesJson: string; + /** + * @generated from field: repeated quixos.orch.ClassCapability class_capabilities = 8; + */ + classCapabilities: ClassCapability[]; }; /** * Describes the message quixos.orch.GetWorkspaceResponse. * Use `create(GetWorkspaceResponseSchema)` to create a new message. */ export declare const GetWorkspaceResponseSchema: GenMessage; +/** + * @generated from message quixos.orch.ClassCapability + */ +export type ClassCapability = Message<"quixos.orch.ClassCapability"> & { + /** + * @generated from field: string conformance_id = 1; + */ + conformanceId: string; + /** + * @generated from field: string atom_id = 2; + */ + atomId: string; + /** + * @generated from field: string interface_revision_id = 3; + */ + interfaceRevisionId: string; + /** + * @generated from field: string definition_id = 4; + */ + definitionId: string; + /** + * @generated from field: string operation_id = 5; + */ + operationId: string; + /** + * @generated from field: string input_type_json = 6; + */ + inputTypeJson: string; + /** + * @generated from field: string output_type_json = 7; + */ + outputTypeJson: string; +}; +/** + * Describes the message quixos.orch.ClassCapability. + * Use `create(ClassCapabilitySchema)` to create a new message. + */ +export declare const ClassCapabilitySchema: GenMessage; /** * @generated from message quixos.orch.CapabilityInputContract */ @@ -516,6 +667,22 @@ export declare const OrchestratorRuntime: GenService<{ input: typeof InvokeCapabilityRequestSchema; output: typeof InvokeCapabilityResponseSchema; }; + /** + * @generated from rpc quixos.orch.OrchestratorRuntime.EditCapabilityField + */ + editCapabilityField: { + methodKind: "unary"; + input: typeof EditCapabilityFieldRequestSchema; + output: typeof InvokeCapabilityResponseSchema; + }; + /** + * @generated from rpc quixos.orch.OrchestratorRuntime.InvokeClassCapability + */ + invokeClassCapability: { + methodKind: "unary"; + input: typeof InvokeClassCapabilityRequestSchema; + output: typeof InvokeCapabilityResponseSchema; + }; /** * @generated from rpc quixos.orch.OrchestratorRuntime.WatchCapability */ diff --git a/dist/quixos/orch_pb.d.ts.map b/dist/quixos/orch_pb.d.ts.map index 9503b29..330cb08 100644 --- a/dist/quixos/orch_pb.d.ts.map +++ b/dist/quixos/orch_pb.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"orch_pb.d.ts","sourceRoot":"","sources":["../../src/quixos/orch_pb.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"orch_pb.d.ts","sourceRoot":"","sources":["../../src/quixos/orch_pb.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/quixos/orch_pb.js b/dist/quixos/orch_pb.js index 1a7d95e..f34bcf7 100644 --- a/dist/quixos/orch_pb.js +++ b/dist/quixos/orch_pb.js @@ -9,7 +9,7 @@ import { file_quixos_runtime } from "./runtime_pb.js"; /** * Describes the file quixos/orch.proto. */ -export const file_quixos_orch = /*@__PURE__*/ fileDesc("ChFxdWl4b3Mvb3JjaC5wcm90bxILcXVpeG9zLm9yY2gipQEKFkNvbnN0cnVjdE9iamVjdFJlcXVlc3QSDwoHYXRvbV9pZBgBIAEoCRI9CgVpbnB1dBgCIAMoCzIuLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlcXVlc3QuSW5wdXRFbnRyeRo7CgpJbnB1dEVudHJ5EgsKA2tleRgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZToCOAEiPwoXQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdCJtCiZSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBIRCglvYmplY3RfaWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJEhEKCW1lbWJlcl9pZBgDIAEoCSJkCidSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdBITCgtjb25zdHJ1Y3RlZBgCIAEoCCLwAQoXSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QSKQoKY2FwYWJpbGl0eRgBIAEoCzIVLnF1aXhvcy5DYXBhYmlsaXR5UmVmEhEKCW9iamVjdF9pZBgCIAEoCRI+CgVpbnB1dBgDIAMoCzIvLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkSGgoSY2xpZW50X211dGF0aW9uX2lkGAQgASgJGjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASLRAQoYSW52b2tlQ2FwYWJpbGl0eVJlc3BvbnNlEhUKDWludm9jYXRpb25faWQYASABKAkSKwoKYWN0aXZhdGlvbhgCIAEoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24SCgoCb2sYAyABKAgSHQoGcmVzdWx0GAQgASgLMg0uY2FtaW5vLlZhbHVlEg0KBWVycm9yGAUgASgJEjcKDGRlcGVuZGVuY2llcxgGIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5ItIBChZXYXRjaENhcGFiaWxpdHlSZXF1ZXN0EikKCmNhcGFiaWxpdHkYASABKAsyFS5xdWl4b3MuQ2FwYWJpbGl0eVJlZhIRCglvYmplY3RfaWQYAiABKAkSPQoFaW5wdXQYAyADKAsyLi5xdWl4b3Mub3JjaC5XYXRjaENhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIuMBChRXYXRjaENhcGFiaWxpdHlFdmVudBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEisKCmFjdGl2YXRpb24YAiABKAsyFy5xdWl4b3Mub3JjaC5BY3RpdmF0aW9uEhAKCHdhdGNoX2lkGAMgASgJEhwKBXZhbHVlGAQgASgLMg0uY2FtaW5vLlZhbHVlEjcKDGRlcGVuZGVuY2llcxgFIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5Eg0KBWVycm9yGAYgASgJEg8KB2luaXRpYWwYByABKAgiFQoTR2V0V29ya3NwYWNlUmVxdWVzdCKXAgoUR2V0V29ya3NwYWNlUmVzcG9uc2USFAoMd29ya3NwYWNlX2lkGAEgASgJEh0KFXdvcmtzcGFjZV9yZXZpc2lvbl9pZBgCIAEoCRIaChJzb3VyY2Vfcm9vdF9jb21taXQYAyABKAkSKgoiZW1wdHlfaW5wdXRfY29uc3RydWN0aWJsZV9hdG9tX2lkcxgEIAMoCRI/ChFjYXBhYmlsaXR5X2lucHV0cxgFIAMoCzIkLnF1aXhvcy5vcmNoLkNhcGFiaWxpdHlJbnB1dENvbnRyYWN0EkEKEmNvbnN0cnVjdG9yX2lucHV0cxgGIAMoCzIlLnF1aXhvcy5vcmNoLkNvbnN0cnVjdG9ySW5wdXRDb250cmFjdCJhChdDYXBhYmlsaXR5SW5wdXRDb250cmFjdBIdChVpbnRlcmZhY2VfcmV2aXNpb25faWQYASABKAkSFAoMb3BlcmF0aW9uX2lkGAIgASgJEhEKCXR5cGVfanNvbhgDIAEoCSI+ChhDb25zdHJ1Y3RvcklucHV0Q29udHJhY3QSDwoHYXRvbV9pZBgBIAEoCRIRCgl0eXBlX2pzb24YAiABKAkiGAoWTGlzdEFjdGl2YXRpb25zUmVxdWVzdCIfCh1MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVxdWVzdCJQCh5MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVzcG9uc2USLgoLZGVzY3JpcHRvcnMYASADKAsyGS5xdWl4b3MuUGFja2FnZURlc2NyaXB0b3IiHAoaTGlzdFBhY2thZ2VSdW50aW1lc1JlcXVlc3QiUgobTGlzdFBhY2thZ2VSdW50aW1lc1Jlc3BvbnNlEjMKCHJ1bnRpbWVzGAEgAygLMiEucXVpeG9zLm9yY2guUGFja2FnZVJ1bnRpbWVTdGF0dXMiRwoXTGlzdEFjdGl2YXRpb25zUmVzcG9uc2USLAoLYWN0aXZhdGlvbnMYASADKAsyFy5xdWl4b3Mub3JjaC5BY3RpdmF0aW9uIj8KFkNsb3NlQWN0aXZhdGlvblJlcXVlc3QSFQoNYWN0aXZhdGlvbl9pZBgBIAEoCRIOCgZyZWFzb24YAiABKAkiRgoXQ2xvc2VBY3RpdmF0aW9uUmVzcG9uc2USKwoKYWN0aXZhdGlvbhgBIAEoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24i6wEKCkFjdGl2YXRpb24SFQoNYWN0aXZhdGlvbl9pZBgBIAEoCRIoCgZleHBvcnQYAiABKAsyGC5xdWl4b3MuUGFja2FnZUV4cG9ydFJlZhIRCglvYmplY3RfaWQYAyABKAkSDQoFc3RhdGUYBCABKAkSDgoGZGVtYW5kGAUgASgNEhEKCW9wZW5lZF9hdBgGIAEoCRIUCgxsYXN0X3VzZWRfYXQYByABKAkSGAoQaWRsZV9kZWFkbGluZV9hdBgIIAEoCRIRCgljbG9zZWRfYXQYCSABKAkSFAoMY2xvc2VfcmVhc29uGAogASgJIrMCChRQYWNrYWdlUnVudGltZVN0YXR1cxITCgtydW50aW1lX2tleRgBIAEoCRIbChNwYWNrYWdlX3JldmlzaW9uX2lkGAIgASgJEhkKEXNvdXJjZV9yZXBvc2l0b3J5GAMgASgJEhUKDXNvdXJjZV9jb21taXQYBCABKAkSFAoMYnVpbGRfdGFyZ2V0GAUgASgJEhMKC3NlcnZlcl9wYXRoGAYgASgJEgsKA3BpZBgHIAEoDRINCgVzdGF0ZRgIIAEoCRISCgpzdGFydGVkX2F0GAkgASgJEhkKEWxhc3RfaGFuZHNoYWtlX2F0GAogASgJEiAKGHJ1bnRpbWVfcHJvdG9jb2xfdmVyc2lvbhgLIAEoCRIfChdhZHZlcnRpc2VkX2V4cG9ydF9jb3VudBgMIAEoDTKuBwoTT3JjaGVzdHJhdG9yUnVudGltZRJfChBJbnZva2VDYXBhYmlsaXR5EiQucXVpeG9zLm9yY2guSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QaJS5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USWwoPV2F0Y2hDYXBhYmlsaXR5EiMucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdBohLnF1aXhvcy5vcmNoLldhdGNoQ2FwYWJpbGl0eUV2ZW50MAESXAoPQ29uc3RydWN0T2JqZWN0EiMucXVpeG9zLm9yY2guQ29uc3RydWN0T2JqZWN0UmVxdWVzdBokLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlc3BvbnNlEowBCh9SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0EjMucXVpeG9zLm9yY2guUmVzb2x2ZU9yQ29uc3RydWN0UmVsYXRlZE9iamVjdFJlcXVlc3QaNC5xdWl4b3Mub3JjaC5SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USUwoMR2V0V29ya3NwYWNlEiAucXVpeG9zLm9yY2guR2V0V29ya3NwYWNlUmVxdWVzdBohLnF1aXhvcy5vcmNoLkdldFdvcmtzcGFjZVJlc3BvbnNlEnEKFkxpc3RQYWNrYWdlRGVzY3JpcHRvcnMSKi5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVxdWVzdBorLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXNwb25zZRJoChNMaXN0UGFja2FnZVJ1bnRpbWVzEicucXVpeG9zLm9yY2guTGlzdFBhY2thZ2VSdW50aW1lc1JlcXVlc3QaKC5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZVJ1bnRpbWVzUmVzcG9uc2USXAoPTGlzdEFjdGl2YXRpb25zEiMucXVpeG9zLm9yY2guTGlzdEFjdGl2YXRpb25zUmVxdWVzdBokLnF1aXhvcy5vcmNoLkxpc3RBY3RpdmF0aW9uc1Jlc3BvbnNlElwKD0Nsb3NlQWN0aXZhdGlvbhIjLnF1aXhvcy5vcmNoLkNsb3NlQWN0aXZhdGlvblJlcXVlc3QaJC5xdWl4b3Mub3JjaC5DbG9zZUFjdGl2YXRpb25SZXNwb25zZWIGcHJvdG8z", [file_camino_api, file_quixos_package, file_quixos_refs, file_quixos_runtime]); +export const file_quixos_orch = /*@__PURE__*/ fileDesc("ChFxdWl4b3Mvb3JjaC5wcm90bxILcXVpeG9zLm9yY2gipQEKFkNvbnN0cnVjdE9iamVjdFJlcXVlc3QSDwoHYXRvbV9pZBgBIAEoCRI9CgVpbnB1dBgCIAMoCzIuLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlcXVlc3QuSW5wdXRFbnRyeRo7CgpJbnB1dEVudHJ5EgsKA2tleRgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZToCOAEiPwoXQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdCJtCiZSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBIRCglvYmplY3RfaWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJEhEKCW1lbWJlcl9pZBgDIAEoCSJkCidSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdBITCgtjb25zdHJ1Y3RlZBgCIAEoCCLwAQoXSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QSKQoKY2FwYWJpbGl0eRgBIAEoCzIVLnF1aXhvcy5DYXBhYmlsaXR5UmVmEhEKCW9iamVjdF9pZBgCIAEoCRI+CgVpbnB1dBgDIAMoCzIvLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkSGgoSY2xpZW50X211dGF0aW9uX2lkGAQgASgJGjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASLOAQocSW52b2tlQ2xhc3NDYXBhYmlsaXR5UmVxdWVzdBIWCg5jb25mb3JtYW5jZV9pZBgBIAEoCRIUCgxvcGVyYXRpb25faWQYAiABKAkSQwoFaW5wdXQYAyADKAsyNC5xdWl4b3Mub3JjaC5JbnZva2VDbGFzc0NhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIoMCChhJbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIrCgphY3RpdmF0aW9uGAIgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbhIKCgJvaxgDIAEoCBIdCgZyZXN1bHQYBCABKAsyDS5jYW1pbm8uVmFsdWUSDQoFZXJyb3IYBSABKAkSNwoMZGVwZW5kZW5jaWVzGAYgAygLMiEucXVpeG9zLnJ1bnRpbWUuRGVyaXZlZERlcGVuZGVuY3kSMAoNZmllbGRfZWRpdGluZxgHIAEoCzIZLnF1aXhvcy5vcmNoLkZpZWxkRWRpdGluZyJ3CgxGaWVsZEVkaXRpbmcSGwoTZ2V0dGVyX29wZXJhdGlvbl9pZBgBIAEoCRIbChNzZXR0ZXJfb3BlcmF0aW9uX2lkGAIgASgJEhUKDWRvY3VtZW50X3R5cGUYAyABKAkSFgoOYmluZGluZ19kaWdlc3QYBCABKAkizgEKGkVkaXRDYXBhYmlsaXR5RmllbGRSZXF1ZXN0EikKCmNhcGFiaWxpdHkYASABKAsyFS5xdWl4b3MuQ2FwYWJpbGl0eVJlZhIRCglvYmplY3RfaWQYAiABKAkSGwoTc2V0dGVyX29wZXJhdGlvbl9pZBgDIAEoCRIWCg5iaW5kaW5nX2RpZ2VzdBgEIAEoCRIhCgZ1cGRhdGUYBSABKAsyES5jYW1pbm8uQ3JkdFZhbHVlEhoKEmNsaWVudF9tdXRhdGlvbl9pZBgGIAEoCSLSAQoWV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdBIpCgpjYXBhYmlsaXR5GAEgASgLMhUucXVpeG9zLkNhcGFiaWxpdHlSZWYSEQoJb2JqZWN0X2lkGAIgASgJEj0KBWlucHV0GAMgAygLMi4ucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdC5JbnB1dEVudHJ5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASKVAgoUV2F0Y2hDYXBhYmlsaXR5RXZlbnQSFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIrCgphY3RpdmF0aW9uGAIgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbhIQCgh3YXRjaF9pZBgDIAEoCRIcCgV2YWx1ZRgEIAEoCzINLmNhbWluby5WYWx1ZRI3CgxkZXBlbmRlbmNpZXMYBSADKAsyIS5xdWl4b3MucnVudGltZS5EZXJpdmVkRGVwZW5kZW5jeRINCgVlcnJvchgGIAEoCRIPCgdpbml0aWFsGAcgASgIEjAKDWZpZWxkX2VkaXRpbmcYCCABKAsyGS5xdWl4b3Mub3JjaC5GaWVsZEVkaXRpbmciOgoTR2V0V29ya3NwYWNlUmVxdWVzdBIjChtpbmNsdWRlX2ludGVyZmFjZV9jb250cmFjdHMYASABKAgi6gIKFEdldFdvcmtzcGFjZVJlc3BvbnNlEhQKDHdvcmtzcGFjZV9pZBgBIAEoCRIdChV3b3Jrc3BhY2VfcmV2aXNpb25faWQYAiABKAkSGgoSc291cmNlX3Jvb3RfY29tbWl0GAMgASgJEioKImVtcHR5X2lucHV0X2NvbnN0cnVjdGlibGVfYXRvbV9pZHMYBCADKAkSPwoRY2FwYWJpbGl0eV9pbnB1dHMYBSADKAsyJC5xdWl4b3Mub3JjaC5DYXBhYmlsaXR5SW5wdXRDb250cmFjdBJBChJjb25zdHJ1Y3Rvcl9pbnB1dHMYBiADKAsyJS5xdWl4b3Mub3JjaC5Db25zdHJ1Y3RvcklucHV0Q29udHJhY3QSFwoPaW50ZXJmYWNlc19qc29uGAcgASgJEjgKEmNsYXNzX2NhcGFiaWxpdGllcxgIIAMoCzIcLnF1aXhvcy5vcmNoLkNsYXNzQ2FwYWJpbGl0eSK5AQoPQ2xhc3NDYXBhYmlsaXR5EhYKDmNvbmZvcm1hbmNlX2lkGAEgASgJEg8KB2F0b21faWQYAiABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAMgASgJEhUKDWRlZmluaXRpb25faWQYBCABKAkSFAoMb3BlcmF0aW9uX2lkGAUgASgJEhcKD2lucHV0X3R5cGVfanNvbhgGIAEoCRIYChBvdXRwdXRfdHlwZV9qc29uGAcgASgJImEKF0NhcGFiaWxpdHlJbnB1dENvbnRyYWN0Eh0KFWludGVyZmFjZV9yZXZpc2lvbl9pZBgBIAEoCRIUCgxvcGVyYXRpb25faWQYAiABKAkSEQoJdHlwZV9qc29uGAMgASgJIj4KGENvbnN0cnVjdG9ySW5wdXRDb250cmFjdBIPCgdhdG9tX2lkGAEgASgJEhEKCXR5cGVfanNvbhgCIAEoCSIYChZMaXN0QWN0aXZhdGlvbnNSZXF1ZXN0Ih8KHUxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXF1ZXN0IlAKHkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXNwb25zZRIuCgtkZXNjcmlwdG9ycxgBIAMoCzIZLnF1aXhvcy5QYWNrYWdlRGVzY3JpcHRvciIcChpMaXN0UGFja2FnZVJ1bnRpbWVzUmVxdWVzdCJSChtMaXN0UGFja2FnZVJ1bnRpbWVzUmVzcG9uc2USMwoIcnVudGltZXMYASADKAsyIS5xdWl4b3Mub3JjaC5QYWNrYWdlUnVudGltZVN0YXR1cyJHChdMaXN0QWN0aXZhdGlvbnNSZXNwb25zZRIsCgthY3RpdmF0aW9ucxgBIAMoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24iPwoWQ2xvc2VBY3RpdmF0aW9uUmVxdWVzdBIVCg1hY3RpdmF0aW9uX2lkGAEgASgJEg4KBnJlYXNvbhgCIAEoCSJGChdDbG9zZUFjdGl2YXRpb25SZXNwb25zZRIrCgphY3RpdmF0aW9uGAEgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbiLrAQoKQWN0aXZhdGlvbhIVCg1hY3RpdmF0aW9uX2lkGAEgASgJEigKBmV4cG9ydBgCIAEoCzIYLnF1aXhvcy5QYWNrYWdlRXhwb3J0UmVmEhEKCW9iamVjdF9pZBgDIAEoCRINCgVzdGF0ZRgEIAEoCRIOCgZkZW1hbmQYBSABKA0SEQoJb3BlbmVkX2F0GAYgASgJEhQKDGxhc3RfdXNlZF9hdBgHIAEoCRIYChBpZGxlX2RlYWRsaW5lX2F0GAggASgJEhEKCWNsb3NlZF9hdBgJIAEoCRIUCgxjbG9zZV9yZWFzb24YCiABKAkiswIKFFBhY2thZ2VSdW50aW1lU3RhdHVzEhMKC3J1bnRpbWVfa2V5GAEgASgJEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYAiABKAkSGQoRc291cmNlX3JlcG9zaXRvcnkYAyABKAkSFQoNc291cmNlX2NvbW1pdBgEIAEoCRIUCgxidWlsZF90YXJnZXQYBSABKAkSEwoLc2VydmVyX3BhdGgYBiABKAkSCwoDcGlkGAcgASgNEg0KBXN0YXRlGAggASgJEhIKCnN0YXJ0ZWRfYXQYCSABKAkSGQoRbGFzdF9oYW5kc2hha2VfYXQYCiABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGAsgASgJEh8KF2FkdmVydGlzZWRfZXhwb3J0X2NvdW50GAwgASgNMoAJChNPcmNoZXN0cmF0b3JSdW50aW1lEl8KEEludm9rZUNhcGFiaWxpdHkSJC5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVxdWVzdBolLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXNwb25zZRJlChNFZGl0Q2FwYWJpbGl0eUZpZWxkEicucXVpeG9zLm9yY2guRWRpdENhcGFiaWxpdHlGaWVsZFJlcXVlc3QaJS5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USaQoVSW52b2tlQ2xhc3NDYXBhYmlsaXR5EikucXVpeG9zLm9yY2guSW52b2tlQ2xhc3NDYXBhYmlsaXR5UmVxdWVzdBolLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXNwb25zZRJbCg9XYXRjaENhcGFiaWxpdHkSIy5xdWl4b3Mub3JjaC5XYXRjaENhcGFiaWxpdHlSZXF1ZXN0GiEucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5RXZlbnQwARJcCg9Db25zdHJ1Y3RPYmplY3QSIy5xdWl4b3Mub3JjaC5Db25zdHJ1Y3RPYmplY3RSZXF1ZXN0GiQucXVpeG9zLm9yY2guQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USjAEKH1Jlc29sdmVPckNvbnN0cnVjdFJlbGF0ZWRPYmplY3QSMy5xdWl4b3Mub3JjaC5SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBo0LnF1aXhvcy5vcmNoLlJlc29sdmVPckNvbnN0cnVjdFJlbGF0ZWRPYmplY3RSZXNwb25zZRJTCgxHZXRXb3Jrc3BhY2USIC5xdWl4b3Mub3JjaC5HZXRXb3Jrc3BhY2VSZXF1ZXN0GiEucXVpeG9zLm9yY2guR2V0V29ya3NwYWNlUmVzcG9uc2UScQoWTGlzdFBhY2thZ2VEZXNjcmlwdG9ycxIqLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXF1ZXN0GisucXVpeG9zLm9yY2guTGlzdFBhY2thZ2VEZXNjcmlwdG9yc1Jlc3BvbnNlEmgKE0xpc3RQYWNrYWdlUnVudGltZXMSJy5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZVJ1bnRpbWVzUmVxdWVzdBooLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlUnVudGltZXNSZXNwb25zZRJcCg9MaXN0QWN0aXZhdGlvbnMSIy5xdWl4b3Mub3JjaC5MaXN0QWN0aXZhdGlvbnNSZXF1ZXN0GiQucXVpeG9zLm9yY2guTGlzdEFjdGl2YXRpb25zUmVzcG9uc2USXAoPQ2xvc2VBY3RpdmF0aW9uEiMucXVpeG9zLm9yY2guQ2xvc2VBY3RpdmF0aW9uUmVxdWVzdBokLnF1aXhvcy5vcmNoLkNsb3NlQWN0aXZhdGlvblJlc3BvbnNlYgZwcm90bzM", [file_camino_api, file_quixos_package, file_quixos_refs, file_quixos_runtime]); /** * Describes the message quixos.orch.ConstructObjectRequest. * Use `create(ConstructObjectRequestSchema)` to create a new message. @@ -35,91 +35,111 @@ export const ResolveOrConstructRelatedObjectResponseSchema = /*@__PURE__*/ messa * Use `create(InvokeCapabilityRequestSchema)` to create a new message. */ export const InvokeCapabilityRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 4); +/** + * Describes the message quixos.orch.InvokeClassCapabilityRequest. + * Use `create(InvokeClassCapabilityRequestSchema)` to create a new message. + */ +export const InvokeClassCapabilityRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 5); /** * Describes the message quixos.orch.InvokeCapabilityResponse. * Use `create(InvokeCapabilityResponseSchema)` to create a new message. */ -export const InvokeCapabilityResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 5); +export const InvokeCapabilityResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 6); +/** + * Describes the message quixos.orch.FieldEditing. + * Use `create(FieldEditingSchema)` to create a new message. + */ +export const FieldEditingSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 7); +/** + * Describes the message quixos.orch.EditCapabilityFieldRequest. + * Use `create(EditCapabilityFieldRequestSchema)` to create a new message. + */ +export const EditCapabilityFieldRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 8); /** * Describes the message quixos.orch.WatchCapabilityRequest. * Use `create(WatchCapabilityRequestSchema)` to create a new message. */ -export const WatchCapabilityRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 6); +export const WatchCapabilityRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 9); /** * Describes the message quixos.orch.WatchCapabilityEvent. * Use `create(WatchCapabilityEventSchema)` to create a new message. */ -export const WatchCapabilityEventSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 7); +export const WatchCapabilityEventSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 10); /** * Describes the message quixos.orch.GetWorkspaceRequest. * Use `create(GetWorkspaceRequestSchema)` to create a new message. */ -export const GetWorkspaceRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 8); +export const GetWorkspaceRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 11); /** * Describes the message quixos.orch.GetWorkspaceResponse. * Use `create(GetWorkspaceResponseSchema)` to create a new message. */ -export const GetWorkspaceResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 9); +export const GetWorkspaceResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 12); +/** + * Describes the message quixos.orch.ClassCapability. + * Use `create(ClassCapabilitySchema)` to create a new message. + */ +export const ClassCapabilitySchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 13); /** * Describes the message quixos.orch.CapabilityInputContract. * Use `create(CapabilityInputContractSchema)` to create a new message. */ -export const CapabilityInputContractSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 10); +export const CapabilityInputContractSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 14); /** * Describes the message quixos.orch.ConstructorInputContract. * Use `create(ConstructorInputContractSchema)` to create a new message. */ -export const ConstructorInputContractSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 11); +export const ConstructorInputContractSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 15); /** * Describes the message quixos.orch.ListActivationsRequest. * Use `create(ListActivationsRequestSchema)` to create a new message. */ -export const ListActivationsRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 12); +export const ListActivationsRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 16); /** * Describes the message quixos.orch.ListPackageDescriptorsRequest. * Use `create(ListPackageDescriptorsRequestSchema)` to create a new message. */ -export const ListPackageDescriptorsRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 13); +export const ListPackageDescriptorsRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 17); /** * Describes the message quixos.orch.ListPackageDescriptorsResponse. * Use `create(ListPackageDescriptorsResponseSchema)` to create a new message. */ -export const ListPackageDescriptorsResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 14); +export const ListPackageDescriptorsResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 18); /** * Describes the message quixos.orch.ListPackageRuntimesRequest. * Use `create(ListPackageRuntimesRequestSchema)` to create a new message. */ -export const ListPackageRuntimesRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 15); +export const ListPackageRuntimesRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 19); /** * Describes the message quixos.orch.ListPackageRuntimesResponse. * Use `create(ListPackageRuntimesResponseSchema)` to create a new message. */ -export const ListPackageRuntimesResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 16); +export const ListPackageRuntimesResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 20); /** * Describes the message quixos.orch.ListActivationsResponse. * Use `create(ListActivationsResponseSchema)` to create a new message. */ -export const ListActivationsResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 17); +export const ListActivationsResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 21); /** * Describes the message quixos.orch.CloseActivationRequest. * Use `create(CloseActivationRequestSchema)` to create a new message. */ -export const CloseActivationRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 18); +export const CloseActivationRequestSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 22); /** * Describes the message quixos.orch.CloseActivationResponse. * Use `create(CloseActivationResponseSchema)` to create a new message. */ -export const CloseActivationResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 19); +export const CloseActivationResponseSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 23); /** * Describes the message quixos.orch.Activation. * Use `create(ActivationSchema)` to create a new message. */ -export const ActivationSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 20); +export const ActivationSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 24); /** * Describes the message quixos.orch.PackageRuntimeStatus. * Use `create(PackageRuntimeStatusSchema)` to create a new message. */ -export const PackageRuntimeStatusSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 21); +export const PackageRuntimeStatusSchema = /*@__PURE__*/ messageDesc(file_quixos_orch, 25); /** * @generated from service quixos.orch.OrchestratorRuntime */ diff --git a/dist/references.d.ts.map b/dist/references.d.ts.map index 9fa618a..1b5f0d3 100644 --- a/dist/references.d.ts.map +++ b/dist/references.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,cAAc,EAAE,OAAO,MAAM,CAAC;AAC5C,MAAM,WAAW,WAAW,CAAC,QAAQ,SAAS,MAAM,GAAG,MAAM;IAC3D,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE;QAAC,QAAQ,EAAE,CAAC,IAAI,QAAQ,GAAG,IAAI;KAAC,CAAC;IAC5D,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;CAC7C;AAQD,eAAO,MAAM,iBAAiB,UAAW,OAAO,KAAG,KAAK,IAAI,WACU,CAAC;AAEvE,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,OAAQ,MAAM,KAAG,WAG9C,CAAC;AACF,eAAO,MAAM,eAAe,UAAW,OAAO,KAAG,MAGhD,CAAC;AACF,eAAO,MAAM,mBAAmB,UAAW,OAAO,yBAA6B,IAO9E,CAAC"} \ No newline at end of file +{"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../src/references.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,cAAc,EAAE,OAAO,MAAM,CAAC;AAC5C,MAAM,WAAW,WAAW,CAAC,QAAQ,SAAS,MAAM,GAAG,MAAM;IAC3D,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE;QAAE,QAAQ,EAAE,CAAC,IAAI,QAAQ,GAAG,IAAI;KAAE,CAAC;IAC9D,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;CAC7C;AAmBD,eAAO,MAAM,iBAAiB,UAAW,OAAO,KAAG,KAAK,IAAI,WACU,CAAC;AAEvE,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,OAAQ,MAAM,KAAG,WAG9C,CAAC;AACF,eAAO,MAAM,eAAe,UAAW,OAAO,KAAG,MAGhD,CAAC;AACF,eAAO,MAAM,mBAAmB,UAAW,OAAO,yBAA6B,IAU9E,CAAC"} \ No newline at end of file diff --git a/dist/references.js b/dist/references.js index 74f234e..44e7c08 100644 --- a/dist/references.js +++ b/dist/references.js @@ -2,11 +2,22 @@ * the raw ID. These handles do not themselves confer authority or a lease. */ const identities = new WeakMap(); class Reference { - constructor(id) { identities.set(this, id); Object.freeze(this); } - equals(other) { return isObjectReference(other) && identities.get(this) === identities.get(other); } - toJSON() { throw new Error("Object references cannot be serialized into ordinary data"); } - toString() { throw new Error("Object references cannot be coerced to strings"); } - [Symbol.toPrimitive]() { throw new Error("Object references cannot be coerced to scalar values"); } + constructor(id) { + identities.set(this, id); + Object.freeze(this); + } + equals(other) { + return isObjectReference(other) && identities.get(this) === identities.get(other); + } + toJSON() { + throw new Error("Object references cannot be serialized into ordinary data"); + } + toString() { + throw new Error("Object references cannot be coerced to strings"); + } + [Symbol.toPrimitive]() { + throw new Error("Object references cannot be coerced to scalar values"); + } } export const isObjectReference = (value) => typeof value === "object" && value !== null && identities.has(value); /** Internal transport boundary; intentionally not exported from the SDK entry. */ diff --git a/dist/relationships.d.ts.map b/dist/relationships.d.ts.map index 022dfdf..e9fbb45 100644 --- a/dist/relationships.d.ts.map +++ b/dist/relationships.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../src/relationships.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAC,sBAAsB,EAAE,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAC1E,KAAK,GAAG,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AACrC,KAAK,IAAI,CAAC,CAAC,SAAS,WAAW,IAAI;IAAC,UAAU,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;IAAC,OAAO,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAA;CAAC,CAAC;AAM9L,+EAA+E;AAC/E,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,GAAG,GAAG,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC;IACvF,IAAI;IACE,GAAG,MAAM,CAAC;;;;IACV,GAAG,MAAM,CAAC,UAAU,CAAC,oBAAoB,MAAM;IAO/C,MAAM,MAAM,CAAC,oBAAoB,MAAM;CAI7C,CAAC;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,WAAW,QAAQ,IAAI,CAAC,CAAC,CAAC;IACnE,IAAI;IACE,MAAM,QAAQ,MAAM,UAAU,CAAC,oBAAoB,MAAM;IAMzD,IAAI,SAAS,MAAM,SAAS,MAAM,oBAAoB,MAAM;IAQ5D,MAAM,SAAS,MAAM,oBAAoB,MAAM;CAKrD,CAAC;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,WAAW,QAAQ,IAAI,CAAC,CAAC,CAAC;IAClE,IAAI;IACE,GAAG,SAAS,CAAC,oBAAoB,MAAM;IAKvC,MAAM,SAAS,CAAC,oBAAoB,MAAM;CAIhD,CAAC"} \ No newline at end of file +{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../src/relationships.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC5E,KAAK,GAAG,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AACrC,KAAK,IAAI,CAAC,CAAC,SAAS,WAAW,IAAI;IACjC,UAAU,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;CACxG,CAAC;AAMF,+EAA+E;AAC/E,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,GAAG,GAAG,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC;IACvF,IAAI;IACE,GAAG,MAAM,CAAC;;;;IAIV,GAAG,MAAM,CAAC,UAAU,CAAC,oBAAoB,MAAM;IAO/C,MAAM,MAAM,CAAC,oBAAoB,MAAM;CAO7C,CAAC;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,WAAW,QAAQ,IAAI,CAAC,CAAC,CAAC;IACnE,IAAI;IACE,MAAM,QAAQ,MAAM,UAAU,CAAC,oBAAoB,MAAM;IAOzD,IAAI,SAAS,MAAM,SAAS,MAAM,oBAAoB,MAAM;IAS5D,MAAM,SAAS,MAAM,oBAAoB,MAAM;CAQrD,CAAC;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,WAAW,QAAQ,IAAI,CAAC,CAAC,CAAC;IAClE,IAAI;IACE,GAAG,SAAS,CAAC,oBAAoB,MAAM;IAKvC,MAAM,SAAS,CAAC,oBAAoB,MAAM;CAOhD,CAAC"} \ No newline at end of file diff --git a/dist/relationships.js b/dist/relationships.js index 9f1cb21..2b4a066 100644 --- a/dist/relationships.js +++ b/dist/relationships.js @@ -7,7 +7,10 @@ const checked = async (port, revision) => { /** Helpers never retry a failed CAS or silently overwrite concurrent edits. */ export const relationshipMap = (port) => ({ read: () => port.collection(), - async get(key) { const snapshot = await port.collection(); return { revision: snapshot.revision, value: snapshot.entries.find((entry) => entry.key === key)?.target }; }, + async get(key) { + const snapshot = await port.collection(); + return { revision: snapshot.revision, value: snapshot.entries.find((entry) => entry.key === key)?.target }; + }, async set(key, target, expectedRevision) { const snapshot = await checked(port, expectedRevision); const entries = snapshot.entries.filter((entry) => entry.key !== key); diff --git a/flake.lock b/flake.lock index 747f7ea..23b2946 100644 --- a/flake.lock +++ b/flake.lock @@ -74,17 +74,17 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1789426470, - "narHash": "sha256-THZZ+S5Uxi2yRw5NwIfyGNn5IUJiVe9qb4271e4loD4=", - "ref": "refs/tags/quixos-reachability/c16271510afaab9b3972b82e6936aa4c5edf790f", - "rev": "c16271510afaab9b3972b82e6936aa4c5edf790f", - "revCount": 57, + "lastModified": 1789602549, + "narHash": "sha256-/riIaX4zEud3wPtGqI5pGm+SbHCrsqVTLZOacfOdYM4=", + "ref": "refs/tags/quixos-reachability/499b65b274f07d151c61e7710929a6323b6549a7", + "rev": "499b65b274f07d151c61e7710929a6323b6549a7", + "revCount": 74, "type": "git", "url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git" }, "original": { - "ref": "refs/tags/quixos-reachability/c16271510afaab9b3972b82e6936aa4c5edf790f", - "rev": "c16271510afaab9b3972b82e6936aa4c5edf790f", + "ref": "refs/tags/quixos-reachability/499b65b274f07d151c61e7710929a6323b6549a7", + "rev": "499b65b274f07d151c61e7710929a6323b6549a7", "type": "git", "url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git" } diff --git a/flake.nix b/flake.nix index 180460d..8f5abc4 100644 --- a/flake.nix +++ b/flake.nix @@ -4,14 +4,20 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; - quixos-protocol.url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git?ref=refs/tags/quixos-reachability/c16271510afaab9b3972b82e6936aa4c5edf790f&rev=c16271510afaab9b3972b82e6936aa4c5edf790f"; + quixos-protocol.url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git?ref=refs/tags/quixos-reachability/499b65b274f07d151c61e7710929a6323b6549a7&rev=499b65b274f07d151c61e7710929a6323b6549a7"; quixosNixHelpers = { url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git?ref=refs/tags/quixos-reachability/7177130c0365f2fa58ea4877366e1c5d17db4c01&rev=7177130c0365f2fa58ea4877366e1c5d17db4c01"; flake = false; }; }; - outputs = inputs@{ nixpkgs, flake-utils, quixosNixHelpers, ... }: + outputs = + inputs@{ + nixpkgs, + flake-utils, + quixosNixHelpers, + ... + }: let quixosHelpers = import "${quixosNixHelpers}/quixos-package-helpers.nix"; packageOutputs = quixosHelpers.mkCaminoTsYarnNixifyFlake { @@ -22,18 +28,37 @@ nativeBuildInputs = { pkgs, ... }: [ pkgs.protobuf ]; - buildEnv = { inputs, pkgs, system }: { - QUIXOS_PROTO_PATH = "${pkgs.protobuf}/include:${inputs.quixos-protocol.packages.${system}.default}/proto"; - }; + buildEnv = + { + inputs, + pkgs, + system, + }: + { + QUIXOS_PROTO_PATH = "${pkgs.protobuf}/include:${ + inputs.quixos-protocol.packages.${system}.default + }/proto"; + }; devShellPackages = { pkgs, ... }: [ pkgs.protobuf ]; - devShellHook = { inputs, pkgs, system, ... }: '' - export QUIXOS_PROTO_PATH="${pkgs.protobuf}/include:${inputs.quixos-protocol.packages.${system}.default}/proto''${QUIXOS_PROTO_PATH:+:$QUIXOS_PROTO_PATH}" - ''; + devShellHook = + { + inputs, + pkgs, + system, + ... + }: + '' + export QUIXOS_PROTO_PATH="${pkgs.protobuf}/include:${ + inputs.quixos-protocol.packages.${system}.default + }/proto''${QUIXOS_PROTO_PATH:+:$QUIXOS_PROTO_PATH}" + ''; }; in - packageOutputs // flake-utils.lib.eachDefaultSystem (system: + packageOutputs + // flake-utils.lib.eachDefaultSystem ( + system: let pkgs = import nixpkgs { inherit system; }; builtPackage = packageOutputs.packages.${system}.default; @@ -45,5 +70,6 @@ ${builtPackage}/libexec/-quixos-camino-package-runtime/dist touch "$out" ''; - }); + } + ); } diff --git a/src/bindings.ts b/src/bindings.ts index 87db362..dffd670 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -1,7 +1,14 @@ import { create } from "@bufbuild/protobuf"; import { ValueSchema, ObjectValueSchema, type Value } from "./camino/api_pb.js"; -import { derived, jsToProtoValue, liveValue, protoValueToJs, - type RuntimeContext, type RuntimeHandler, type DerivedHandler } from "./index.js"; +import { + derived, + jsToProtoValue, + liveValue, + protoValueToJs, + type RuntimeContext, + type RuntimeHandler, + type DerivedHandler, +} from "./index.js"; export type { QxObjectRef } from "./references.js"; import { assertReferenceFree, referenceToWire } from "./references.js"; @@ -9,24 +16,11 @@ declare const watchBrand: unique symbol; export type QxWatchHandle = string & { readonly [watchBrand]: true }; export type MessageBinding = { encode(value: T): Value; decode(value: Value): T }; export type QxLiveValue = ReturnType; -const reactPropsDescriptor = "org.quixos.web-studio.ReactProps"; -// Explicit temporary props exception, matching orch's RPC contract. Field shapes -// remain unchecked pending generics; ordinary messages and state do not gain it. -export const opaqueReactPropsBinding: MessageBinding> = { - encode(value) { - if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("React props must be an object"); - return jsToProtoValue(value); - }, - decode(value) { - if (value.kind.case !== "objectValue") throw new Error("React props must be an object"); - return protoValueToJs(value) as Record; - }, -}; export type BindingValue = B extends MessageBinding ? T : never; export type QxHandler = (context: C) => O | Promise; export type QxDerived = { kind: "derived"; get: QxHandler }; -export type QxSession = {id: string; run(work: (context: C) => Promise): Promise; close(): Promise}; -export type QxContextLifecycle = {signal?: AbortSignal; openSession?: () => Promise>}; +export type QxSession = { id: string; run(work: (context: C) => Promise): Promise; close(): Promise }; +export type QxContextLifecycle = { signal?: AbortSignal; openSession?: () => Promise> }; export const qxDerived = (get: QxHandler): QxDerived => ({ kind: "derived", get }); /** Versioned binding ABI. This mirrors the language-neutral value IR. */ @@ -44,7 +38,10 @@ export type QxPortSpec = | { kind: "interface"; id: string; operations: Record } | { kind: "constructor"; id: string; inputType: QxValueType }; export type QxHandlerSpec = { - inputType: QxValueType; outputType: QxValueType; eventType?: QxValueType; + receiver?: "none"; + inputType: QxValueType; + outputType: QxValueType; + eventType?: QxValueType; ports: Record; }; export type QxMessages = Record>; @@ -57,18 +54,22 @@ export const decodeQxValue = (type: QxValueType, value: Value | undefined, messa if (type.kind === "record") { if (value.kind.case !== "objectValue") throw new Error("Expected QX record"); const fields = value.kind.value.fields; - if (Object.keys(fields).some((name) => !Object.hasOwn(type.fields, name))) throw new Error("Unexpected QX record field"); - return Object.fromEntries(Object.entries(type.fields).map(([name, field]) => [name, decodeQxValue(field, fields[name], messages)])); + if (Object.keys(fields).some((name) => !Object.hasOwn(type.fields, name))) + throw new Error("Unexpected QX record field"); + return Object.fromEntries( + Object.entries(type.fields).map(([name, field]) => [name, decodeQxValue(field, fields[name], messages)]), + ); } - if (type.kind === "optional") return value.kind.case === "nullValue" ? null : decodeQxValue(type.value, value, messages); + if (type.kind === "optional") + return value.kind.case === "nullValue" ? null : decodeQxValue(type.value, value, messages); if (type.kind === "list") { if (value.kind.case !== "listValue") throw new Error("Expected QX list"); return value.kind.value.values.map((entry) => decodeQxValue(type.value, entry, messages)); } if (type.kind === "message") { - if (type.descriptorId !== reactPropsDescriptor) assertReferenceFree(protoValueToJs(value)); + assertReferenceFree(protoValueToJs(value)); const decoded = requireMessage(messages, type.descriptorId).decode(value); - if (type.descriptorId !== reactPropsDescriptor) assertReferenceFree(decoded); + assertReferenceFree(decoded); return decoded; } if (type.kind === "object-ref") { @@ -98,28 +99,39 @@ export const encodeQxValue = (type: QxValueType, value: any, messages: QxMessage if (type.kind === "builtin" && type.name === "unit") return jsToProtoValue(null); if (type.kind === "record") { if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected QX record"); - if (Object.keys(value).some((name) => !Object.hasOwn(type.fields, name))) throw new Error("Unexpected QX record field"); - const fields = Object.fromEntries(Object.entries(type.fields).map(([name, field]) => { - if (!Object.hasOwn(value, name) && field.kind !== "optional") throw new Error(`Missing QX record field ${name}`); - return [name, encodeQxValue(field, value[name] ?? (field.kind === "optional" ? null : value[name]), messages)]; - })); - return create(ValueSchema, {kind: {case: "objectValue", value: create(ObjectValueSchema, {fields})}}); + if (Object.keys(value).some((name) => !Object.hasOwn(type.fields, name))) + throw new Error("Unexpected QX record field"); + const fields = Object.fromEntries( + Object.entries(type.fields).map(([name, field]) => { + if (!Object.hasOwn(value, name) && field.kind !== "optional") + throw new Error(`Missing QX record field ${name}`); + return [name, encodeQxValue(field, value[name] ?? (field.kind === "optional" ? null : value[name]), messages)]; + }), + ); + return create(ValueSchema, { kind: { case: "objectValue", value: create(ObjectValueSchema, { fields }) } }); } - if (type.kind === "optional") return value === null ? jsToProtoValue(null) : encodeQxValue(type.value, value, messages); - if (type.kind === "list") return jsToProtoValue(value.map((entry: unknown) => liveValue(encodeQxValue(type.value, entry, messages)))); - if (type.kind === "object-ref") { referenceToWire(value); return jsToProtoValue(value); } - if (type.kind !== "message" || type.descriptorId !== reactPropsDescriptor) assertReferenceFree(value); + if (type.kind === "optional") + return value === null ? jsToProtoValue(null) : encodeQxValue(type.value, value, messages); + if (type.kind === "list") + return jsToProtoValue(value.map((entry: unknown) => liveValue(encodeQxValue(type.value, entry, messages)))); + if (type.kind === "object-ref") { + referenceToWire(value); + return jsToProtoValue(value); + } + assertReferenceFree(value); if (type.kind === "message") { const encoded = requireMessage(messages, type.descriptorId).encode(value); - if (type.descriptorId !== reactPropsDescriptor) assertReferenceFree(protoValueToJs(encoded)); + assertReferenceFree(protoValueToJs(encoded)); return encoded; } return jsToProtoValue(value); }; const inputValue = (context: RuntimeContext, type: QxValueType) => { - if (type.kind === "message" || type.kind === "record") return create(ValueSchema, { kind: { case: "objectValue", - value: create(ObjectValueSchema, { fields: context.inputProto }) } }); + if (type.kind === "message" || type.kind === "record") + return create(ValueSchema, { + kind: { case: "objectValue", value: create(ObjectValueSchema, { fields: context.inputProto }) }, + }); return context.inputProto.value; }; const inputFields = (type: QxValueType, value: unknown, messages: QxMessages): Record => { @@ -134,45 +146,110 @@ const inputFields = (type: QxValueType, value: unknown, messages: QxMessages): R /** The sole unchecked cast connects generated contracts to the dynamic RPC runtime. */ export const bindQxHandler = ( - spec: QxHandlerSpec, handler: QxHandler | QxDerived, messages: QxMessages, + spec: QxHandlerSpec, + handler: QxHandler | QxDerived, + messages: QxMessages, ): RuntimeHandler | DerivedHandler => { const bindContext = (raw: RuntimeContext): C => { - const ports = Object.fromEntries(Object.entries(spec.ports).map(([name, port]) => { - switch (port.kind) { - case "state": { - const state = raw.state(port.id); - return [name, { - ...(port.primitives.includes("read") ? { get: async () => decodeQxValue(port.valueType, (await state.live()).$quixosValue, messages), live: () => state.live() } : {}), - ...(port.primitives.includes("write") ? { set: async (value: unknown) => state.set(liveValue(encodeQxValue(port.valueType, value, messages))) } : {}), - }]; + const ports = Object.fromEntries( + Object.entries(spec.ports).map(([name, port]) => { + switch (port.kind) { + case "state": { + const state = raw.state(port.id); + return [ + name, + { + ...(port.primitives.includes("read") + ? { + get: async () => decodeQxValue(port.valueType, (await state.live()).$quixosValue, messages), + live: () => state.live(), + } + : {}), + ...(port.primitives.includes("write") + ? { + set: async (value: unknown) => + state.set(liveValue(encodeQxValue(port.valueType, value, messages))), + } + : {}), + }, + ]; + } + case "edge": { + const edge = raw.edge(port.id); + return [ + name, + { + ...Object.fromEntries( + port.primitives.map((primitive) => [ + primitive, + edge[primitive as "resolve" | "connect" | "disconnect"], + ]), + ), + ...(port.primitives.includes("resolve") ? { collection: edge.collection } : {}), + ...(port.primitives.includes("resolve") && + port.primitives.includes("connect") && + port.primitives.includes("disconnect") + ? { replace: edge.replace } + : {}), + }, + ]; + } + case "interface": { + const target = raw.interface(port.id); + return [ + name, + { + objectId: target.objectId, + live: Object.fromEntries( + Object.entries(port.operations).map(([name, operation]) => [ + name, + (input: unknown) => target.live(operation.id, inputFields(operation.inputType, input, messages)), + ]), + ), + ...Object.fromEntries( + Object.entries(port.operations).map(([name, operation]) => [ + name, + async (input: unknown) => + decodeQxValue( + operation.outputType, + (await target.live(operation.id, inputFields(operation.inputType, input, messages))) + .$quixosValue, + messages, + ), + ]), + ), + }, + ]; + } + case "constructor": + return [ + name, + { + construct: (input: unknown) => + raw.constructor(port.id).construct(inputFields(port.inputType, input, messages)), + }, + ]; } - case "edge": { - const edge = raw.edge(port.id); - return [name, {...Object.fromEntries(port.primitives.map((primitive) => [primitive, edge[primitive as "resolve" | "connect" | "disconnect"]])), - ...(port.primitives.includes("resolve") ? {collection: edge.collection} : {}), - ...(port.primitives.includes("resolve") && port.primitives.includes("connect") && port.primitives.includes("disconnect") ? {replace: edge.replace} : {})}]; - } - case "interface": { - const target = raw.interface(port.id); - return [name, {objectId: target.objectId, - live: Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [name, - (input: unknown) => target.live(operation.id, inputFields(operation.inputType, input, messages)), - ])), - ...Object.fromEntries(Object.entries(port.operations).map(([name, operation]) => [name, - async (input: unknown) => decodeQxValue(operation.outputType, - (await target.live(operation.id, inputFields(operation.inputType, input, messages))).$quixosValue, messages), - ]))}]; - } - case "constructor": return [name, { construct: (input: unknown) => raw.constructor(port.id).construct(inputFields(port.inputType, input, messages)) }]; - } - })); - return { objectId: raw.objectId, signal: raw.signal, - ...(raw.openSession ? {openSession: async () => { - const session = await raw.openSession!(); - return {id: session.id, close: () => session.close(), - run: (work: (context: C) => Promise) => session.run((next) => work(bindContext(next)))}; - }} : {}), - input: decodeQxValue(spec.inputType, inputValue(raw, spec.inputType), messages), ports } as C; + }), + ); + return { + ...(spec.receiver === "none" ? {} : { objectId: raw.objectId }), + signal: raw.signal, + ...(spec.receiver !== "none" && raw.openSession + ? { + openSession: async () => { + const session = await raw.openSession!(); + return { + id: session.id, + close: () => session.close(), + run: (work: (context: C) => Promise) => session.run((next) => work(bindContext(next))), + }; + }, + } + : {}), + input: decodeQxValue(spec.inputType, inputValue(raw, spec.inputType), messages), + ports, + } as C; }; const execute = async (raw: RuntimeContext) => { const context = bindContext(raw); diff --git a/src/index.ts b/src/index.ts index 42b3113..bc3bc24 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,26 @@ import http from "node:http"; import { readFileSync } from "node:fs"; import { createInvocationRegistry } from "./invocations.js"; -import { isObjectReference, referenceFromWire, referenceToWire, assertReferenceFree, type QxObjectRef } from "./references.js"; +import { + isObjectReference, + referenceFromWire, + referenceToWire, + assertReferenceFree, + type QxObjectRef, +} from "./references.js"; export * from "./bindings.js"; -export {relationshipMap, relationshipList, relationshipSet} from "./relationships.js"; +export { relationshipMap, relationshipList, relationshipSet } from "./relationships.js"; import { AsyncLocalStorage } from "node:async_hooks"; import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto"; -export {createMigrationContext, migrationObjectId, serveMigration, type MigrationContext, type MigrationInput, type MigrationOutput, type MigrationEdge} from "./migration.js"; +export { + createMigrationContext, + migrationObjectId, + serveMigration, + type MigrationContext, + type MigrationInput, + type MigrationOutput, + type MigrationEdge, +} from "./migration.js"; import { create, equals } from "@bufbuild/protobuf"; import { Code, ConnectError, createClient, type Client, type ConnectRouter } from "@connectrpc/connect"; import { connectNodeAdapter, createConnectTransport } from "@connectrpc/connect-node"; @@ -63,14 +77,22 @@ const base64ToBytes = (value: string) => Buffer.from(value, "base64"); const isRecord = (value: unknown): value is Record => Boolean(value) && typeof value === "object" && !Array.isArray(value); const isWrappedValue = (value: unknown): value is { $quixosValue: Value } => - isRecord(value) && "$quixosValue" in value && - isRecord(value.$quixosValue) && value.$quixosValue.$typeName === "camino.Value"; + isRecord(value) && + "$quixosValue" in value && + isRecord(value.$quixosValue) && + value.$quixosValue.$typeName === "camino.Value"; -export const objectRef = (reference: QxObjectRef) => { referenceToWire(reference); return reference; }; +export const objectRef = (reference: QxObjectRef) => { + referenceToWire(reference); + return reference; +}; export const liveValue = (value: Value) => ({ $quixosValue: value }); export const jsToProtoValue = (value: unknown): Value => { - if (isObjectReference(value)) return create(ValueSchema, {kind: {case: "refValue", value: create(RefValueSchema, {objectId: referenceToWire(value)})}}); + if (isObjectReference(value)) + return create(ValueSchema, { + kind: { case: "refValue", value: create(RefValueSchema, { objectId: referenceToWire(value) }) }, + }); if (isWrappedValue(value)) return value.$quixosValue; if (value === null || value === undefined) { return create(ValueSchema, { kind: { case: "nullValue", value: create(NullValueSchema, {}) } }); @@ -86,43 +108,55 @@ export const jsToProtoValue = (value: unknown): Value => { }); } if (isRecord(value) && "$quixosRef" in value) throw new Error("Raw ID wrappers are not object references"); - if (isRecord(value) && typeof value.$quixosCrdtType === "string" && - typeof value.$quixosCrdtPayload === "string") { + if (isRecord(value) && typeof value.$quixosCrdtType === "string" && typeof value.$quixosCrdtPayload === "string") { return create(ValueSchema, { - kind: { case: "crdtValue", value: create(CrdtValueSchema, { - type: value.$quixosCrdtType, - encoding: typeof value.$quixosCrdtEncoding === "string" ? value.$quixosCrdtEncoding : "base64", - payload: base64ToBytes(value.$quixosCrdtPayload), - }) }, + kind: { + case: "crdtValue", + value: create(CrdtValueSchema, { + type: value.$quixosCrdtType, + encoding: typeof value.$quixosCrdtEncoding === "string" ? value.$quixosCrdtEncoding : "base64", + payload: base64ToBytes(value.$quixosCrdtPayload), + }), + }, }); } if (!isRecord(value)) throw new Error(`Unsupported runtime value ${typeof value}`); return create(ValueSchema, { - kind: { case: "objectValue", value: create(ObjectValueSchema, { - fields: Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, jsToProtoValue(entry)])), - }) }, + kind: { + case: "objectValue", + value: create(ObjectValueSchema, { + fields: Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, jsToProtoValue(entry)])), + }), + }, }); }; export const protoValueToJs = (value: Value | undefined): unknown => { switch (value?.kind.case) { case "nullValue": - case undefined: return null; + case undefined: + return null; case "boolValue": case "numberValue": case "stringValue": - case "integerValue": return value.kind.value; - case "bytesValue": return bytesToBase64(value.kind.value); - case "refValue": return referenceFromWire(value.kind.value.objectId); - case "listValue": return value.kind.value.values.map(protoValueToJs); - case "objectValue": return Object.fromEntries( - Object.entries(value.kind.value.fields).map(([key, entry]) => [key, protoValueToJs(entry)]), - ); - case "crdtValue": return { - $quixosCrdtType: value.kind.value.type, - $quixosCrdtEncoding: value.kind.value.encoding, - $quixosCrdtPayload: bytesToBase64(value.kind.value.payload), - }; + case "integerValue": + return value.kind.value; + case "bytesValue": + return bytesToBase64(value.kind.value); + case "refValue": + return referenceFromWire(value.kind.value.objectId); + case "listValue": + return value.kind.value.values.map(protoValueToJs); + case "objectValue": + return Object.fromEntries( + Object.entries(value.kind.value.fields).map(([key, entry]) => [key, protoValueToJs(entry)]), + ); + case "crdtValue": + return { + $quixosCrdtType: value.kind.value.type, + $quixosCrdtEncoding: value.kind.value.encoding, + $quixosCrdtPayload: bytesToBase64(value.kind.value.payload), + }; } }; @@ -144,8 +178,15 @@ export type EdgePort = { collection(): Promise; replace(entries: RelationshipEntry[], expectedRevision: bigint): Promise; }; -export type RelationshipEntry = {edgeId?: string; target: T; key?: string | boolean | bigint}; -export type RelationshipCollection = {revision: bigint; entries: RelationshipEntry[]}; +export type RelationshipEntry = { + edgeId?: string; + target: T; + key?: string | boolean | bigint; +}; +export type RelationshipCollection = { + revision: bigint; + entries: RelationshipEntry[]; +}; export type InterfacePort = { objectId: QxObjectRef; interfaceRevisionId: string; @@ -180,13 +221,17 @@ export type RuntimeSession = { }; export class RuntimeAuthorityError extends Error { readonly retryable: boolean; - constructor(message: string) { super(message); this.name = "RuntimeAuthorityError"; this.retryable = /WORKSPACE_FENCED|STALE_EPOCH/.test(message); } + constructor(message: string) { + super(message); + this.name = "RuntimeAuthorityError"; + this.retryable = /WORKSPACE_FENCED|STALE_EPOCH/.test(message); + } } const targetForEdge = ( edge: { firstObjectId: string; secondObjectId: string; firstProjectionId: string }, projectionId: string, -) => edge.firstProjectionId === projectionId ? edge.secondObjectId : edge.firstObjectId; +) => (edge.firstProjectionId === projectionId ? edge.secondObjectId : edge.firstObjectId); export const createRuntimeContext = ( camino: CaminoClient, @@ -222,25 +267,63 @@ export const createRuntimeContext = ( case "edge": { const { edgeTypeId, projectionId } = dependency.binding.value; const dependencyObjectId = dependency.objectId || request.objectId; - const collectionResult = (response: {revision: bigint; entries: {edgeId: string; targetObjectId: string; key?: Value}[]}): RelationshipCollection => ({revision: response.revision, - entries: response.entries.map((entry) => ({edgeId: entry.edgeId, target: referenceFromWire(entry.targetObjectId), - ...(entry.key ? {key: entry.key.kind.case === "integerValue" ? BigInt(entry.key.kind.value) : protoValueToJs(entry.key) as string | boolean} : {})}))}); + const collectionResult = (response: { + revision: bigint; + entries: { edgeId: string; targetObjectId: string; key?: Value }[]; + }): RelationshipCollection => ({ + revision: response.revision, + entries: response.entries.map((entry) => ({ + edgeId: entry.edgeId, + target: referenceFromWire(entry.targetObjectId), + ...(entry.key + ? { + key: + entry.key.kind.case === "integerValue" + ? BigInt(entry.key.kind.value) + : (protoValueToJs(entry.key) as string | boolean), + } + : {}), + })), + }); const edge: EdgePort = { edgeTypeId, projectionId, async collection() { - await recordDependency({kind: "edge", objectId: dependencyObjectId, attachmentId: edgeTypeId, projectionId}); - return collectionResult(await camino.readCollection({objectId: dependencyObjectId, edgeTypeId, projectionId})); + await recordDependency({ + kind: "edge", + objectId: dependencyObjectId, + attachmentId: edgeTypeId, + projectionId, + }); + return collectionResult( + await camino.readCollection({ objectId: dependencyObjectId, edgeTypeId, projectionId }), + ); }, async replace(entries, expectedRevision) { - return collectionResult(await camino.replaceCollection({objectId: dependencyObjectId, edgeTypeId, projectionId, expectedRevision, - entries: entries.map((entry) => { - assertReferenceFree(entry.key); - return {edgeId: entry.edgeId ?? "", targetObjectId: referenceToWire(entry.target), key: entry.key === undefined ? undefined : jsToProtoValue(entry.key)}; - })})); + return collectionResult( + await camino.replaceCollection({ + objectId: dependencyObjectId, + edgeTypeId, + projectionId, + expectedRevision, + entries: entries.map((entry) => { + assertReferenceFree(entry.key); + return { + edgeId: entry.edgeId ?? "", + targetObjectId: referenceToWire(entry.target), + key: entry.key === undefined ? undefined : jsToProtoValue(entry.key), + }; + }), + }), + ); }, async resolve() { - await recordDependency({ kind: "edge", objectId: dependencyObjectId, attachmentId: edgeTypeId, projectionId }); + await recordDependency({ + kind: "edge", + objectId: dependencyObjectId, + attachmentId: edgeTypeId, + projectionId, + }); const result = await camino.resolveEdge({ objectId: dependencyObjectId, edgeTypeId, projectionId }); return result.edges.map((entry) => referenceFromWire(targetForEdge(entry, projectionId))); }, @@ -252,7 +335,8 @@ export const createRuntimeContext = ( const targetObjectId = referenceToWire(target); const result = await camino.resolveEdge({ objectId: dependencyObjectId, edgeTypeId, projectionId }); for (const entry of result.edges) { - if (targetForEdge(entry, projectionId) === targetObjectId) await camino.disconnectEdge({ edgeId: entry.id }); + if (targetForEdge(entry, projectionId) === targetObjectId) + await camino.disconnectEdge({ edgeId: entry.id }); } }, }; @@ -325,7 +409,9 @@ export const createRuntimeContext = ( return port as T; }; return { - objectId: referenceFromWire(request.objectId), + get objectId() { + return referenceFromWire(request.objectId); + }, input: protoFieldsToJs(request.input), inputProto: request.input, ports, @@ -348,20 +434,21 @@ const evaluate = async ( observe?: (dependency: RuntimeDependency) => Promise, ) => { const dependencies = new Map(); - const result = await dependencyScope.run( - { dependencies, observe }, - () => isDerived(handler) ? handler.get(context) : handler(context), + const result = await dependencyScope.run({ dependencies, observe }, () => + isDerived(handler) ? handler.get(context) : handler(context), ); return { value: jsToProtoValue(result), dependencies: [...dependencies.values()] }; }; -const protoDependencies = (dependencies: RuntimeDependency[]) => dependencies.map((entry) => - create(DerivedDependencySchema, { - kind: entry.kind, - objectId: entry.objectId, - attachmentId: entry.attachmentId, - projectionId: entry.kind === "edge" ? entry.projectionId : "", - })); +const protoDependencies = (dependencies: RuntimeDependency[]) => + dependencies.map((entry) => + create(DerivedDependencySchema, { + kind: entry.kind, + objectId: entry.objectId, + attachmentId: entry.attachmentId, + projectionId: entry.kind === "edge" ? entry.projectionId : "", + }), + ); export const createPackageRuntimeRoutes = (config: { packageRevisionId: string; @@ -371,27 +458,38 @@ export const createPackageRuntimeRoutes = (config: { }) => { const invocations = createInvocationRegistry(); const headers: Record = {}; - const processToken = process.env.CAMINO_RUNTIME_AUTH_TOKEN ?? (process.env.CAMINO_RUNTIME_AUTH_TOKEN_FILE - ? readFileSync(process.env.CAMINO_RUNTIME_AUTH_TOKEN_FILE, "utf8").trim() : ""); + const processToken = + process.env.CAMINO_RUNTIME_AUTH_TOKEN ?? + (process.env.CAMINO_RUNTIME_AUTH_TOKEN_FILE + ? readFileSync(process.env.CAMINO_RUNTIME_AUTH_TOKEN_FILE, "utf8").trim() + : ""); if (processToken) { headers["x-camino-runtime-token"] = processToken; } else if (process.env.CAMINO_RUNTIME_AUTH_REQUIRED === "1") { throw new Error("CAMINO_RUNTIME_AUTH_TOKEN is required"); } - const camino = createClient(CaminoService, createConnectTransport({ - baseUrl: config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310", - httpVersion: "1.1", - interceptors: headers["x-camino-runtime-token"] ? [ - (next) => async (request) => { - request.header.set("x-camino-runtime-token", headers["x-camino-runtime-token"]!); - return await next(request); - }, - ] : [], - })); - const orch = createClient(OrchestratorRuntime, createConnectTransport({ - baseUrl: config.orchUrl ?? process.env.QUIXOS_ORCH_URL ?? "http://127.0.0.1:7311", - httpVersion: "1.1", - })); + const camino = createClient( + CaminoService, + createConnectTransport({ + baseUrl: config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310", + httpVersion: "1.1", + interceptors: headers["x-camino-runtime-token"] + ? [ + (next) => async (request) => { + request.header.set("x-camino-runtime-token", headers["x-camino-runtime-token"]!); + return await next(request); + }, + ] + : [], + }), + ); + const orch = createClient( + OrchestratorRuntime, + createConnectTransport({ + baseUrl: config.orchUrl ?? process.env.QUIXOS_ORCH_URL ?? "http://127.0.0.1:7311", + httpVersion: "1.1", + }), + ); const authenticateInstance = (header: Headers) => { if (!process.env.QUIXOS_RUNTIME_INSTANCE_ID) return; // standalone development ABI @@ -403,35 +501,67 @@ export const createPackageRuntimeRoutes = (config: { }; const clientsFor = (request: { context?: { grant: string; instanceId: string; workspaceEpoch: string } }) => { const context = request.context; - if (process.env.QUIXOS_RUNTIME_INSTANCE_ID && (!context?.grant || context.instanceId !== process.env.QUIXOS_RUNTIME_INSTANCE_ID || !context.workspaceEpoch)) { + if ( + process.env.QUIXOS_RUNTIME_INSTANCE_ID && + (!context?.grant || context.instanceId !== process.env.QUIXOS_RUNTIME_INSTANCE_ID || !context.workspaceEpoch) + ) { throw new ConnectError("Managed invocation requires an exact instance and epoch grant", Code.Unauthenticated); } if (!context?.grant) return { camino, orch }; - const transport = (url: string) => createConnectTransport({ baseUrl: url, httpVersion: "1.1", interceptors: [(next) => async (call) => { - call.header.set("x-quixos-invocation-grant", context.grant); - call.header.set("x-camino-runtime-token", processToken); - return next(call); - }] }); + const transport = (url: string) => + createConnectTransport({ + baseUrl: url, + httpVersion: "1.1", + interceptors: [ + (next) => async (call) => { + call.header.set("x-quixos-invocation-grant", context.grant); + call.header.set("x-camino-runtime-token", processToken); + return next(call); + }, + ], + }); return { - camino: createClient(CaminoService, transport(config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310")), - orch: createClient(OrchestratorRuntime, transport(config.orchUrl ?? process.env.QUIXOS_ORCH_URL ?? "http://127.0.0.1:7311")), + camino: createClient( + CaminoService, + transport(config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310"), + ), + orch: createClient( + OrchestratorRuntime, + transport(config.orchUrl ?? process.env.QUIXOS_ORCH_URL ?? "http://127.0.0.1:7311"), + ), }; }; const runtimeControl = async (operation: string, input: unknown): Promise => { - const response = await fetch(`${config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310"}/__runtime/${operation}`, { - method: "POST", headers: { "content-type": "application/json", "x-camino-runtime-token": processToken }, body: JSON.stringify(input), signal: AbortSignal.timeout(10_000), - }); - const value = await response.json() as T & {error?: string}; + const response = await fetch( + `${config.caminoUrl ?? process.env.CAMINO_URL ?? "http://127.0.0.1:7310"}/__runtime/${operation}`, + { + method: "POST", + headers: { "content-type": "application/json", "x-camino-runtime-token": processToken }, + body: JSON.stringify(input), + signal: AbortSignal.timeout(10_000), + }, + ); + const value = (await response.json()) as T & { error?: string }; if (!response.ok) throw new RuntimeAuthorityError(value.error ?? "Runtime authority request failed"); return value; }; - const attachSessions = (runtimeContext: RuntimeContext, request: RuntimeRequest & {context?: {grant: string; instanceId: string; workspaceEpoch: string; ownerConformanceId?: string}}) => { + const attachSessions = ( + runtimeContext: RuntimeContext, + request: RuntimeRequest & { + context?: { grant: string; instanceId: string; workspaceEpoch: string; ownerConformanceId?: string }; + }, + ) => { if (!request.context?.grant || !request.context.ownerConformanceId) return; runtimeContext.openSession = async () => { const ownerId = request.context!.ownerConformanceId!; - const registration = { grant: request.context!.grant, objectId: request.objectId, ownerId, - sessionId: `session:${randomBytes(16).toString("hex")}`, token: randomBytes(32).toString("base64url") }; - const register = () => runtimeControl<{sessionId: string; token: string}>("register-session", registration); + const registration = { + grant: request.context!.grant, + objectId: request.objectId, + ownerId, + sessionId: `session:${randomBytes(16).toString("hex")}`, + token: randomBytes(32).toString("base64url"), + }; + const register = () => runtimeControl<{ sessionId: string; token: string }>("register-session", registration); const registered = await register().catch((error) => { // Retry a transport/lost-response failure with exactly the same identity. // Admission/authority errors are definitive and must not be retried here. @@ -445,210 +575,246 @@ export const createPackageRuntimeRoutes = (config: { if (closed) throw new RuntimeAuthorityError("SESSION_CLOSED"); // Acquisition happens before user code. A fence failure can be retried // by the caller without replaying a side-effecting callback. - const grant = await runtimeControl<{grant: string; epoch: string; instanceId: string; invocationId: string; bindingDigest: string}>("acquire-session", registered); + const grant = await runtimeControl<{ + grant: string; + epoch: string; + instanceId: string; + invocationId: string; + bindingDigest: string; + }>("acquire-session", registered); const execution = invocations.begin(grant.invocationId); - const sessionRequest = { ...request, context: { grant: grant.grant, instanceId: grant.instanceId, workspaceEpoch: grant.epoch } }; + const sessionRequest = { + ...request, + context: { grant: grant.grant, instanceId: grant.instanceId, workspaceEpoch: grant.epoch }, + }; const clients = clientsFor(sessionRequest); const context = createRuntimeContext(clients.camino, clients.orch, sessionRequest); context.signal = execution.signal; - try { return await work(context); } - finally { + try { + return await work(context); + } finally { execution.finish(); - await runtimeControl("complete-invocation", { invocationId: grant.invocationId }).catch((error) => console.error("Session completion will be reconciled by the host", error)); + await runtimeControl("complete-invocation", { invocationId: grant.invocationId }).catch((error) => + console.error("Session completion will be reconciled by the host", error), + ); } }, - async close() { await runtimeControl("close-session", registered); closed = true; }, + async close() { + await runtimeControl("close-session", registered); + closed = true; + }, }; }; }; - return (router: ConnectRouter) => router.service(PackageRuntime, { - handshake: (request) => create(HandshakeResponseSchema, { - packageRevisionId: config.packageRevisionId, - runtimeProtocolVersion: "quixos-capabilities-v1", - exportIds: Object.keys(config.exports), - capabilities: ["invocation-completion-v1", "instance-authentication-v1", "epoch-grants-v1"], - instanceId: process.env.QUIXOS_RUNTIME_INSTANCE_ID ?? "", - authenticationProof: request.nonce && processToken ? createHmac("sha256", processToken) - .update(JSON.stringify([request.nonce, process.env.QUIXOS_RUNTIME_INSTANCE_ID ?? "", config.packageRevisionId])) - .digest("hex") : "", - }), - getInvocationStatus: (request, context) => { - authenticateInstance(context.requestHeader); - return invocations.status(request.invocationId); - }, - cancelInvocation: (request, context) => { - authenticateInstance(context.requestHeader); - return invocations.cancel(request.invocationId); - }, - invoke: async (request, context) => { - authenticateInstance(context.requestHeader); - const { camino, orch } = clientsFor(request); - const exportId = request.export?.exportId; - const handler = exportId ? config.exports[exportId] : undefined; - if (!handler) throw new ConnectError(`Unknown export ${exportId ?? ""}`, Code.NotFound); - const execution = invocations.begin(request.invocationId || (process.env.QUIXOS_RUNTIME_INSTANCE_ID ? "" : randomUUID())); - try { - const runtimeContext = createRuntimeContext(camino, orch, request); - attachSessions(runtimeContext, request); - runtimeContext.signal = AbortSignal.any([context.signal, execution.signal]); - const result = await evaluate(handler, runtimeContext); - execution.finish(); - return create(InvokeResponseSchema, { - ok: true, - result: result.value, - dependencies: protoDependencies(result.dependencies), - }); - } catch (error) { - execution.finish(true); - return create(InvokeResponseSchema, { - ok: false, - error: error instanceof Error ? error.message : String(error), - }); - } - }, - watch: async function* (request, context) { - authenticateInstance(context.requestHeader); - const { camino, orch } = clientsFor(request); - const exportId = request.export?.exportId; - const handler = exportId ? config.exports[exportId] : undefined; - if (!handler || !isDerived(handler)) { - throw new ConnectError(`Export ${exportId ?? ""} is not derived`, Code.FailedPrecondition); - } - const execution = invocations.begin(request.invocationId || (process.env.QUIXOS_RUNTIME_INSTANCE_ID ? "" : randomUUID())); - const signal = AbortSignal.any([context.signal, execution.signal]); - try { - const watchId = `watch:${randomUUID()}`; - const runtimeContext = createRuntimeContext(camino, orch, request); - attachSessions(runtimeContext, request); - runtimeContext.signal = signal; - type WatchOutcome = { key: string; done: boolean; error?: unknown }; - type Subscription = { - dependency: RuntimeDependency; - controller: AbortController; - next: Promise; - waitNext: () => Promise; - }; - const subscriptions = new Map(); - const establishing = new Map>(); - let subscriptionEpoch = 0; - - const ensureSubscription = async (dependency: RuntimeDependency) => { - const key = dependencyKey(dependency); - if (subscriptions.has(key)) return; - const pending = establishing.get(key); - if (pending) return await pending; - const establish = (async () => { - const controller = new AbortController(); - const stream = camino.watchObject( - { objectId: dependency.objectId, includeSnapshot: true, attachmentIds: request.context?.grant ? [dependency.attachmentId] : [] }, - { signal: controller.signal }, - )[Symbol.asyncIterator](); - try { - // Camino subscribes before producing the snapshot, so once this - // resolves the following state/edge read cannot race the stream. - const snapshot = await stream.next(); - if (snapshot.done) throw new Error(`Dependency stream ${key} ended during setup`); - const waitNext = () => stream.next().then( - (result) => ({ key, done: Boolean(result.done) }), - (error: unknown) => ({ key, done: true, error }), - ); - const subscription: Subscription = { - dependency, - controller, - waitNext, - next: Promise.resolve({ key, done: false }), - }; - subscription.next = waitNext(); - subscriptions.set(key, subscription); - subscriptionEpoch += 1; - } catch (error) { - controller.abort(); - throw error; - } - })(); - establishing.set(key, establish); + return (router: ConnectRouter) => + router.service(PackageRuntime, { + handshake: (request) => + create(HandshakeResponseSchema, { + packageRevisionId: config.packageRevisionId, + runtimeProtocolVersion: "quixos-capabilities-v1", + exportIds: Object.keys(config.exports), + capabilities: ["invocation-completion-v1", "instance-authentication-v1", "epoch-grants-v1"], + instanceId: process.env.QUIXOS_RUNTIME_INSTANCE_ID ?? "", + authenticationProof: + request.nonce && processToken + ? createHmac("sha256", processToken) + .update( + JSON.stringify([ + request.nonce, + process.env.QUIXOS_RUNTIME_INSTANCE_ID ?? "", + config.packageRevisionId, + ]), + ) + .digest("hex") + : "", + }), + getInvocationStatus: (request, context) => { + authenticateInstance(context.requestHeader); + return invocations.status(request.invocationId); + }, + cancelInvocation: (request, context) => { + authenticateInstance(context.requestHeader); + return invocations.cancel(request.invocationId); + }, + invoke: async (request, context) => { + authenticateInstance(context.requestHeader); + const { camino, orch } = clientsFor(request); + const exportId = request.export?.exportId; + const handler = exportId ? config.exports[exportId] : undefined; + if (!handler) throw new ConnectError(`Unknown export ${exportId ?? ""}`, Code.NotFound); + const execution = invocations.begin( + request.invocationId || (process.env.QUIXOS_RUNTIME_INSTANCE_ID ? "" : randomUUID()), + ); try { - await establish; - } finally { - establishing.delete(key); + const runtimeContext = createRuntimeContext(camino, orch, request); + attachSessions(runtimeContext, request); + runtimeContext.signal = AbortSignal.any([context.signal, execution.signal]); + const result = await evaluate(handler, runtimeContext); + execution.finish(); + return create(InvokeResponseSchema, { + ok: true, + result: result.value, + dependencies: protoDependencies(result.dependencies), + }); + } catch (error) { + execution.finish(true); + return create(InvokeResponseSchema, { + ok: false, + error: error instanceof Error ? error.message : String(error), + }); } - }; - - const abortAll = () => { - for (const subscription of subscriptions.values()) { - subscription.controller.abort(); + }, + watch: async function* (request, context) { + authenticateInstance(context.requestHeader); + const { camino, orch } = clientsFor(request); + const exportId = request.export?.exportId; + const handler = exportId ? config.exports[exportId] : undefined; + if (!handler || !isDerived(handler)) { + throw new ConnectError(`Export ${exportId ?? ""} is not derived`, Code.FailedPrecondition); } - }; - signal.addEventListener("abort", abortAll, { once: true }); + const execution = invocations.begin( + request.invocationId || (process.env.QUIXOS_RUNTIME_INSTANCE_ID ? "" : randomUUID()), + ); + const signal = AbortSignal.any([context.signal, execution.signal]); + try { + const watchId = `watch:${randomUUID()}`; + const runtimeContext = createRuntimeContext(camino, orch, request); + attachSessions(runtimeContext, request); + runtimeContext.signal = signal; + type WatchOutcome = { key: string; done: boolean; error?: unknown }; + type Subscription = { + dependency: RuntimeDependency; + controller: AbortController; + next: Promise; + waitNext: () => Promise; + }; + const subscriptions = new Map(); + const establishing = new Map>(); + let subscriptionEpoch = 0; - const evaluateWithStableSubscriptions = async () => { - // A direct state/edge port records its dependency before reading it, - // but a nested interface invocation can only report its transitive - // dependencies after that invocation returns. Once a new dependency - // stream is established, evaluate again so every read contributing to - // the emitted value happened after its stream became live. - for (let pass = 0; pass < 32; pass += 1) { - const before = subscriptionEpoch; - const result = await evaluate(handler, runtimeContext, ensureSubscription); - if (subscriptionEpoch === before) return result; - } - throw new Error("Derived dependency discovery did not stabilize after 32 passes"); - }; - - try { - let current = await evaluateWithStableSubscriptions(); - yield create(WatchEventSchema, { - watchId, - value: current.value, - dependencies: protoDependencies(current.dependencies), - initial: true, - }); - - const abort = new Promise<"abort">((resolve) => { - if (signal.aborted) resolve("abort"); - else signal.addEventListener("abort", () => resolve("abort"), { once: true }); - }); - while (!signal.aborted) { - if (subscriptions.size === 0) { - await abort; - break; - } - const outcome = await Promise.race([ - ...[...subscriptions.values()].map((entry) => entry.next), - abort, - ]); - if (outcome === "abort") break; - const subscription = subscriptions.get(outcome.key); - if (!subscription) continue; - if (outcome.error) throw outcome.error; - if (outcome.done) throw new Error(`Dependency stream ${outcome.key} ended unexpectedly`); - subscription.next = subscription.waitNext(); - - const updated = await evaluateWithStableSubscriptions(); - const active = new Set(updated.dependencies.map(dependencyKey)); - for (const [key, entry] of subscriptions) { - if (!active.has(key)) { - entry.controller.abort(); - subscriptions.delete(key); + const ensureSubscription = async (dependency: RuntimeDependency) => { + const key = dependencyKey(dependency); + if (subscriptions.has(key)) return; + const pending = establishing.get(key); + if (pending) return await pending; + const establish = (async () => { + const controller = new AbortController(); + const stream = camino + .watchObject( + { + objectId: dependency.objectId, + includeSnapshot: true, + attachmentIds: request.context?.grant ? [dependency.attachmentId] : [], + }, + { signal: controller.signal }, + ) + [Symbol.asyncIterator](); + try { + // Camino subscribes before producing the snapshot, so once this + // resolves the following state/edge read cannot race the stream. + const snapshot = await stream.next(); + if (snapshot.done) throw new Error(`Dependency stream ${key} ended during setup`); + const waitNext = () => + stream.next().then( + (result) => ({ key, done: Boolean(result.done) }), + (error: unknown) => ({ key, done: true, error }), + ); + const subscription: Subscription = { + dependency, + controller, + waitNext, + next: Promise.resolve({ key, done: false }), + }; + subscription.next = waitNext(); + subscriptions.set(key, subscription); + subscriptionEpoch += 1; + } catch (error) { + controller.abort(); + throw error; + } + })(); + establishing.set(key, establish); + try { + await establish; + } finally { + establishing.delete(key); } - } - if (!equals(ValueSchema, current.value, updated.value)) { + }; + + const abortAll = () => { + for (const subscription of subscriptions.values()) { + subscription.controller.abort(); + } + }; + signal.addEventListener("abort", abortAll, { once: true }); + + const evaluateWithStableSubscriptions = async () => { + // A direct state/edge port records its dependency before reading it, + // but a nested interface invocation can only report its transitive + // dependencies after that invocation returns. Once a new dependency + // stream is established, evaluate again so every read contributing to + // the emitted value happened after its stream became live. + for (let pass = 0; pass < 32; pass += 1) { + const before = subscriptionEpoch; + const result = await evaluate(handler, runtimeContext, ensureSubscription); + if (subscriptionEpoch === before) return result; + } + throw new Error("Derived dependency discovery did not stabilize after 32 passes"); + }; + + try { + let current = await evaluateWithStableSubscriptions(); yield create(WatchEventSchema, { watchId, - value: updated.value, - dependencies: protoDependencies(updated.dependencies), + value: current.value, + dependencies: protoDependencies(current.dependencies), + initial: true, }); + + const abort = new Promise<"abort">((resolve) => { + if (signal.aborted) resolve("abort"); + else signal.addEventListener("abort", () => resolve("abort"), { once: true }); + }); + while (!signal.aborted) { + if (subscriptions.size === 0) { + await abort; + break; + } + const outcome = await Promise.race([...[...subscriptions.values()].map((entry) => entry.next), abort]); + if (outcome === "abort") break; + const subscription = subscriptions.get(outcome.key); + if (!subscription) continue; + if (outcome.error) throw outcome.error; + if (outcome.done) throw new Error(`Dependency stream ${outcome.key} ended unexpectedly`); + subscription.next = subscription.waitNext(); + + const updated = await evaluateWithStableSubscriptions(); + const active = new Set(updated.dependencies.map(dependencyKey)); + for (const [key, entry] of subscriptions) { + if (!active.has(key)) { + entry.controller.abort(); + subscriptions.delete(key); + } + } + if (!equals(ValueSchema, current.value, updated.value)) { + yield create(WatchEventSchema, { + watchId, + value: updated.value, + dependencies: protoDependencies(updated.dependencies), + }); + } + current = updated; + } + } finally { + signal.removeEventListener("abort", abortAll); + abortAll(); } - current = updated; + } finally { + execution.finish(); } - } finally { - signal.removeEventListener("abort", abortAll); - abortAll(); - } - } finally { execution.finish(); } - }, - }); + }, + }); }; export const servePackageRuntime = (config: { diff --git a/src/invocations.ts b/src/invocations.ts index c78d9f1..b4443fb 100644 --- a/src/invocations.ts +++ b/src/invocations.ts @@ -3,15 +3,23 @@ export const createInvocationRegistry = () => { const entries = new Map(); return { begin(id: string) { - if (!id || entries.has(id)) throw new Error("Invocation ID is missing or already used; invocations are never replayed implicitly"); + if (!id || entries.has(id)) + throw new Error("Invocation ID is missing or already used; invocations are never replayed implicitly"); // Completed identities remain until process retirement. A bounded process // may reject new work; it must not evict and accidentally replay a call. if (entries.size >= 100_000) throw new Error("Invocation registry full; explicit runtime retirement required"); const entry = { state: "running", controller: new AbortController() }; entries.set(id, entry); - return { signal: entry.controller.signal, finish(failed = false) { entry.state = failed ? "failed" : "completed"; } }; + return { + signal: entry.controller.signal, + finish(failed = false) { + entry.state = failed ? "failed" : "completed"; + }, + }; + }, + status(id: string) { + return { invocationId: id, state: entries.get(id)?.state ?? "unknown" }; }, - status(id: string) { return { invocationId: id, state: entries.get(id)?.state ?? "unknown" }; }, cancel(id: string) { const entry = entries.get(id); if (entry && ["running", "cancellation-requested"].includes(entry.state)) { diff --git a/src/migration.ts b/src/migration.ts index 338e78e..72d0147 100644 --- a/src/migration.ts +++ b/src/migration.ts @@ -1,18 +1,42 @@ -import {createHash} from "node:crypto"; +import { createHash } from "node:crypto"; export type MigrationInput = { - schemaVersion: 1; executionId: string; exportId: string; - ports: {name: string; binding: string; view: "old" | "new"; access: ("read" | "write" | "create" | "edge")[]; atomId?: string; - attachedAtomId?: string; defaultValue?: unknown; - states?: {objectId: string; value: unknown}[]; edges?: MigrationEdge[]}[]; + schemaVersion: 1; + executionId: string; + exportId: string; + ports: { + name: string; + binding: string; + view: "old" | "new"; + access: ("read" | "write" | "create" | "edge")[]; + atomId?: string; + attachedAtomId?: string; + defaultValue?: unknown; + states?: { objectId: string; value: unknown }[]; + edges?: MigrationEdge[]; + }[]; +}; +export type MigrationEdge = { + id: string; + edgeTypeId: string; + firstObjectId: string; + secondObjectId: string; + firstProjectionId: string; + secondProjectionId: string; + firstOrdinal?: number; + secondOrdinal?: number; + firstKeyJson?: string; + secondKeyJson?: string; +}; +export type MigrationOutput = { + schemaVersion: 1; + executionId: string; + writes: { port: string; objectId: string; value: unknown }[]; + creates: { port: string; logicalKey: string; objectId: string }[]; + edgeReplacements: { port: string; edges: MigrationEdge[] }[]; }; -export type MigrationEdge = {id: string; edgeTypeId: string; firstObjectId: string; secondObjectId: string; firstProjectionId: string; secondProjectionId: string; firstOrdinal?: number; secondOrdinal?: number; firstKeyJson?: string; secondKeyJson?: string}; -export type MigrationOutput = {schemaVersion: 1; executionId: string; - writes: {port: string; objectId: string; value: unknown}[]; - creates: {port: string; logicalKey: string; objectId: string}[]; - edgeReplacements: {port: string; edges: MigrationEdge[]}[]}; export type MigrationContext = { - enumerate(port: string): {objectId: string; value: unknown}[]; + enumerate(port: string): { objectId: string; value: unknown }[]; read(port: string, objectId: string): unknown; write(port: string, objectId: string, value: unknown): void; create(port: string, logicalKey: string): string; @@ -20,65 +44,100 @@ export type MigrationContext = { replaceEdges(port: string, edges: MigrationEdge[]): void; }; export const migrationObjectId = (executionId: string, port: string, logicalKey: string) => - `obj:migration:${createHash("sha256").update(JSON.stringify([executionId, port, logicalKey])).digest("hex")}`; + `obj:migration:${createHash("sha256") + .update(JSON.stringify([executionId, port, logicalKey])) + .digest("hex")}`; /** No ordinary RuntimeContext or network/database clients are supplied here. * Process isolation belongs to the host, not this convenience API. */ export const createMigrationContext = (input: MigrationInput) => { - if (input.schemaVersion !== 1 || !input.executionId || new Set(input.ports.map((entry) => entry.name)).size !== input.ports.length) throw new Error("Invalid migration input"); - const output: MigrationOutput = {schemaVersion: 1, executionId: input.executionId, writes: [], creates: [], edgeReplacements: []}; + if ( + input.schemaVersion !== 1 || + !input.executionId || + new Set(input.ports.map((entry) => entry.name)).size !== input.ports.length + ) + throw new Error("Invalid migration input"); + const output: MigrationOutput = { + schemaVersion: 1, + executionId: input.executionId, + writes: [], + creates: [], + edgeReplacements: [], + }; const port = (name: string, access: string) => { const selected = input.ports.find((entry) => entry.name === name); - if (!selected?.access.includes(access as "read") || (selected.view === "old" && access !== "read")) throw new Error(`Migration port ${name} does not grant ${access}`); + if (!selected?.access.includes(access as "read") || (selected.view === "old" && access !== "read")) + throw new Error(`Migration port ${name} does not grant ${access}`); return selected; }; const context: MigrationContext = { enumerate(name) { - const selected = port(name, "read"), states = structuredClone(selected.states ?? []); - if (selected.view === "new" && Object.hasOwn(selected, "defaultValue")) for (const helper of output.creates) { - if (input.ports.find((entry) => entry.name === helper.port)?.atomId === selected.attachedAtomId && !states.some((entry) => entry.objectId === helper.objectId)) states.push({objectId: helper.objectId, value: structuredClone(selected.defaultValue)}); - } - if (selected.view === "new") for (const write of output.writes) { - if (input.ports.find((entry) => entry.name === write.port)?.binding !== selected.binding) continue; - const existing = states.findIndex((entry) => entry.objectId === write.objectId), entry = {objectId: write.objectId, value: structuredClone(write.value)}; - if (existing < 0) states.push(entry); else states[existing] = entry; - } - return states.sort((a, b) => a.objectId < b.objectId ? -1 : a.objectId > b.objectId ? 1 : 0); + const selected = port(name, "read"), + states = structuredClone(selected.states ?? []); + if (selected.view === "new" && Object.hasOwn(selected, "defaultValue")) + for (const helper of output.creates) { + if ( + input.ports.find((entry) => entry.name === helper.port)?.atomId === selected.attachedAtomId && + !states.some((entry) => entry.objectId === helper.objectId) + ) + states.push({ objectId: helper.objectId, value: structuredClone(selected.defaultValue) }); + } + if (selected.view === "new") + for (const write of output.writes) { + if (input.ports.find((entry) => entry.name === write.port)?.binding !== selected.binding) continue; + const existing = states.findIndex((entry) => entry.objectId === write.objectId), + entry = { objectId: write.objectId, value: structuredClone(write.value) }; + if (existing < 0) states.push(entry); + else states[existing] = entry; + } + return states.sort((a, b) => (a.objectId < b.objectId ? -1 : a.objectId > b.objectId ? 1 : 0)); + }, + read(name, objectId) { + return context.enumerate(name).find((entry) => entry.objectId === objectId)?.value; }, - read(name, objectId) {return context.enumerate(name).find((entry) => entry.objectId === objectId)?.value;}, write(name, objectId, value) { port(name, "write"); const previous = output.writes.findIndex((entry) => entry.port === name && entry.objectId === objectId); - const entry = {port: name, objectId, value: structuredClone(value)}; - if (previous < 0) output.writes.push(entry); else output.writes[previous] = entry; + const entry = { port: name, objectId, value: structuredClone(value) }; + if (previous < 0) output.writes.push(entry); + else output.writes[previous] = entry; }, create(name, logicalKey) { port(name, "create"); - if (!logicalKey || logicalKey.length > 1024) throw new Error("Migration creation requires a bounded stable logical key"); + if (!logicalKey || logicalKey.length > 1024) + throw new Error("Migration creation requires a bounded stable logical key"); const objectId = migrationObjectId(input.executionId, name, logicalKey); - if (!output.creates.some((entry) => entry.objectId === objectId)) output.creates.push({port: name, logicalKey, objectId}); + if (!output.creates.some((entry) => entry.objectId === objectId)) + output.creates.push({ port: name, logicalKey, objectId }); return objectId; }, edges(name) { const selected = port(name, "read"); - const replacement = selected.view === "new" ? output.edgeReplacements.find((entry) => input.ports.find((candidate) => candidate.name === entry.port)?.binding === selected.binding) : undefined; + const replacement = + selected.view === "new" + ? output.edgeReplacements.find( + (entry) => input.ports.find((candidate) => candidate.name === entry.port)?.binding === selected.binding, + ) + : undefined; return structuredClone(replacement?.edges ?? selected.edges ?? []); }, replaceEdges(name, edges) { port(name, "edge"); const previous = output.edgeReplacements.findIndex((entry) => entry.port === name); - const entry = {port: name, edges: structuredClone(edges)}; - if (previous < 0) output.edgeReplacements.push(entry); else output.edgeReplacements[previous] = entry; + const entry = { port: name, edges: structuredClone(edges) }; + if (previous < 0) output.edgeReplacements.push(entry); + else output.edgeReplacements[previous] = entry; }, }; - return {context, result: () => structuredClone(output)}; + return { context, result: () => structuredClone(output) }; }; /** Entrypoint for an immutable package's dedicated bin/migrate executable. * stdout is protocol-only; send diagnostics to stderr. The host independently * validates every write, helper identity, contract, and completion receipt. */ export const serveMigration = async (exports: Record void | Promise>) => { - const chunks: Buffer[] = []; let bytes = 0; + const chunks: Buffer[] = []; + let bytes = 0; for await (const chunk of process.stdin) { bytes += chunk.length; if (bytes > 16 * 1024 * 1024) throw new Error("Migration input exceeds 16 MiB"); diff --git a/src/quixos/orch_pb.ts b/src/quixos/orch_pb.ts index 02b2bca..be53edb 100644 --- a/src/quixos/orch_pb.ts +++ b/src/quixos/orch_pb.ts @@ -4,7 +4,7 @@ import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; -import type { CaminoObject, Value } from "../camino/api_pb.js"; +import type { CaminoObject, CrdtValue, Value } from "../camino/api_pb.js"; import { file_camino_api } from "../camino/api_pb.js"; import type { PackageDescriptor } from "./package_pb.js"; import { file_quixos_package } from "./package_pb.js"; @@ -18,7 +18,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file quixos/orch.proto. */ export const file_quixos_orch: GenFile = /*@__PURE__*/ - fileDesc("ChFxdWl4b3Mvb3JjaC5wcm90bxILcXVpeG9zLm9yY2gipQEKFkNvbnN0cnVjdE9iamVjdFJlcXVlc3QSDwoHYXRvbV9pZBgBIAEoCRI9CgVpbnB1dBgCIAMoCzIuLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlcXVlc3QuSW5wdXRFbnRyeRo7CgpJbnB1dEVudHJ5EgsKA2tleRgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZToCOAEiPwoXQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdCJtCiZSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBIRCglvYmplY3RfaWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJEhEKCW1lbWJlcl9pZBgDIAEoCSJkCidSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdBITCgtjb25zdHJ1Y3RlZBgCIAEoCCLwAQoXSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QSKQoKY2FwYWJpbGl0eRgBIAEoCzIVLnF1aXhvcy5DYXBhYmlsaXR5UmVmEhEKCW9iamVjdF9pZBgCIAEoCRI+CgVpbnB1dBgDIAMoCzIvLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkSGgoSY2xpZW50X211dGF0aW9uX2lkGAQgASgJGjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASLRAQoYSW52b2tlQ2FwYWJpbGl0eVJlc3BvbnNlEhUKDWludm9jYXRpb25faWQYASABKAkSKwoKYWN0aXZhdGlvbhgCIAEoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24SCgoCb2sYAyABKAgSHQoGcmVzdWx0GAQgASgLMg0uY2FtaW5vLlZhbHVlEg0KBWVycm9yGAUgASgJEjcKDGRlcGVuZGVuY2llcxgGIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5ItIBChZXYXRjaENhcGFiaWxpdHlSZXF1ZXN0EikKCmNhcGFiaWxpdHkYASABKAsyFS5xdWl4b3MuQ2FwYWJpbGl0eVJlZhIRCglvYmplY3RfaWQYAiABKAkSPQoFaW5wdXQYAyADKAsyLi5xdWl4b3Mub3JjaC5XYXRjaENhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIuMBChRXYXRjaENhcGFiaWxpdHlFdmVudBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEisKCmFjdGl2YXRpb24YAiABKAsyFy5xdWl4b3Mub3JjaC5BY3RpdmF0aW9uEhAKCHdhdGNoX2lkGAMgASgJEhwKBXZhbHVlGAQgASgLMg0uY2FtaW5vLlZhbHVlEjcKDGRlcGVuZGVuY2llcxgFIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5Eg0KBWVycm9yGAYgASgJEg8KB2luaXRpYWwYByABKAgiFQoTR2V0V29ya3NwYWNlUmVxdWVzdCKXAgoUR2V0V29ya3NwYWNlUmVzcG9uc2USFAoMd29ya3NwYWNlX2lkGAEgASgJEh0KFXdvcmtzcGFjZV9yZXZpc2lvbl9pZBgCIAEoCRIaChJzb3VyY2Vfcm9vdF9jb21taXQYAyABKAkSKgoiZW1wdHlfaW5wdXRfY29uc3RydWN0aWJsZV9hdG9tX2lkcxgEIAMoCRI/ChFjYXBhYmlsaXR5X2lucHV0cxgFIAMoCzIkLnF1aXhvcy5vcmNoLkNhcGFiaWxpdHlJbnB1dENvbnRyYWN0EkEKEmNvbnN0cnVjdG9yX2lucHV0cxgGIAMoCzIlLnF1aXhvcy5vcmNoLkNvbnN0cnVjdG9ySW5wdXRDb250cmFjdCJhChdDYXBhYmlsaXR5SW5wdXRDb250cmFjdBIdChVpbnRlcmZhY2VfcmV2aXNpb25faWQYASABKAkSFAoMb3BlcmF0aW9uX2lkGAIgASgJEhEKCXR5cGVfanNvbhgDIAEoCSI+ChhDb25zdHJ1Y3RvcklucHV0Q29udHJhY3QSDwoHYXRvbV9pZBgBIAEoCRIRCgl0eXBlX2pzb24YAiABKAkiGAoWTGlzdEFjdGl2YXRpb25zUmVxdWVzdCIfCh1MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVxdWVzdCJQCh5MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVzcG9uc2USLgoLZGVzY3JpcHRvcnMYASADKAsyGS5xdWl4b3MuUGFja2FnZURlc2NyaXB0b3IiHAoaTGlzdFBhY2thZ2VSdW50aW1lc1JlcXVlc3QiUgobTGlzdFBhY2thZ2VSdW50aW1lc1Jlc3BvbnNlEjMKCHJ1bnRpbWVzGAEgAygLMiEucXVpeG9zLm9yY2guUGFja2FnZVJ1bnRpbWVTdGF0dXMiRwoXTGlzdEFjdGl2YXRpb25zUmVzcG9uc2USLAoLYWN0aXZhdGlvbnMYASADKAsyFy5xdWl4b3Mub3JjaC5BY3RpdmF0aW9uIj8KFkNsb3NlQWN0aXZhdGlvblJlcXVlc3QSFQoNYWN0aXZhdGlvbl9pZBgBIAEoCRIOCgZyZWFzb24YAiABKAkiRgoXQ2xvc2VBY3RpdmF0aW9uUmVzcG9uc2USKwoKYWN0aXZhdGlvbhgBIAEoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24i6wEKCkFjdGl2YXRpb24SFQoNYWN0aXZhdGlvbl9pZBgBIAEoCRIoCgZleHBvcnQYAiABKAsyGC5xdWl4b3MuUGFja2FnZUV4cG9ydFJlZhIRCglvYmplY3RfaWQYAyABKAkSDQoFc3RhdGUYBCABKAkSDgoGZGVtYW5kGAUgASgNEhEKCW9wZW5lZF9hdBgGIAEoCRIUCgxsYXN0X3VzZWRfYXQYByABKAkSGAoQaWRsZV9kZWFkbGluZV9hdBgIIAEoCRIRCgljbG9zZWRfYXQYCSABKAkSFAoMY2xvc2VfcmVhc29uGAogASgJIrMCChRQYWNrYWdlUnVudGltZVN0YXR1cxITCgtydW50aW1lX2tleRgBIAEoCRIbChNwYWNrYWdlX3JldmlzaW9uX2lkGAIgASgJEhkKEXNvdXJjZV9yZXBvc2l0b3J5GAMgASgJEhUKDXNvdXJjZV9jb21taXQYBCABKAkSFAoMYnVpbGRfdGFyZ2V0GAUgASgJEhMKC3NlcnZlcl9wYXRoGAYgASgJEgsKA3BpZBgHIAEoDRINCgVzdGF0ZRgIIAEoCRISCgpzdGFydGVkX2F0GAkgASgJEhkKEWxhc3RfaGFuZHNoYWtlX2F0GAogASgJEiAKGHJ1bnRpbWVfcHJvdG9jb2xfdmVyc2lvbhgLIAEoCRIfChdhZHZlcnRpc2VkX2V4cG9ydF9jb3VudBgMIAEoDTKuBwoTT3JjaGVzdHJhdG9yUnVudGltZRJfChBJbnZva2VDYXBhYmlsaXR5EiQucXVpeG9zLm9yY2guSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QaJS5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USWwoPV2F0Y2hDYXBhYmlsaXR5EiMucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdBohLnF1aXhvcy5vcmNoLldhdGNoQ2FwYWJpbGl0eUV2ZW50MAESXAoPQ29uc3RydWN0T2JqZWN0EiMucXVpeG9zLm9yY2guQ29uc3RydWN0T2JqZWN0UmVxdWVzdBokLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlc3BvbnNlEowBCh9SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0EjMucXVpeG9zLm9yY2guUmVzb2x2ZU9yQ29uc3RydWN0UmVsYXRlZE9iamVjdFJlcXVlc3QaNC5xdWl4b3Mub3JjaC5SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USUwoMR2V0V29ya3NwYWNlEiAucXVpeG9zLm9yY2guR2V0V29ya3NwYWNlUmVxdWVzdBohLnF1aXhvcy5vcmNoLkdldFdvcmtzcGFjZVJlc3BvbnNlEnEKFkxpc3RQYWNrYWdlRGVzY3JpcHRvcnMSKi5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVxdWVzdBorLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXNwb25zZRJoChNMaXN0UGFja2FnZVJ1bnRpbWVzEicucXVpeG9zLm9yY2guTGlzdFBhY2thZ2VSdW50aW1lc1JlcXVlc3QaKC5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZVJ1bnRpbWVzUmVzcG9uc2USXAoPTGlzdEFjdGl2YXRpb25zEiMucXVpeG9zLm9yY2guTGlzdEFjdGl2YXRpb25zUmVxdWVzdBokLnF1aXhvcy5vcmNoLkxpc3RBY3RpdmF0aW9uc1Jlc3BvbnNlElwKD0Nsb3NlQWN0aXZhdGlvbhIjLnF1aXhvcy5vcmNoLkNsb3NlQWN0aXZhdGlvblJlcXVlc3QaJC5xdWl4b3Mub3JjaC5DbG9zZUFjdGl2YXRpb25SZXNwb25zZWIGcHJvdG8z", [file_camino_api, file_quixos_package, file_quixos_refs, file_quixos_runtime]); + fileDesc("ChFxdWl4b3Mvb3JjaC5wcm90bxILcXVpeG9zLm9yY2gipQEKFkNvbnN0cnVjdE9iamVjdFJlcXVlc3QSDwoHYXRvbV9pZBgBIAEoCRI9CgVpbnB1dBgCIAMoCzIuLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlcXVlc3QuSW5wdXRFbnRyeRo7CgpJbnB1dEVudHJ5EgsKA2tleRgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZToCOAEiPwoXQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdCJtCiZSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBIRCglvYmplY3RfaWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJEhEKCW1lbWJlcl9pZBgDIAEoCSJkCidSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdBITCgtjb25zdHJ1Y3RlZBgCIAEoCCLwAQoXSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QSKQoKY2FwYWJpbGl0eRgBIAEoCzIVLnF1aXhvcy5DYXBhYmlsaXR5UmVmEhEKCW9iamVjdF9pZBgCIAEoCRI+CgVpbnB1dBgDIAMoCzIvLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkSGgoSY2xpZW50X211dGF0aW9uX2lkGAQgASgJGjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASLOAQocSW52b2tlQ2xhc3NDYXBhYmlsaXR5UmVxdWVzdBIWCg5jb25mb3JtYW5jZV9pZBgBIAEoCRIUCgxvcGVyYXRpb25faWQYAiABKAkSQwoFaW5wdXQYAyADKAsyNC5xdWl4b3Mub3JjaC5JbnZva2VDbGFzc0NhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIoMCChhJbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIrCgphY3RpdmF0aW9uGAIgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbhIKCgJvaxgDIAEoCBIdCgZyZXN1bHQYBCABKAsyDS5jYW1pbm8uVmFsdWUSDQoFZXJyb3IYBSABKAkSNwoMZGVwZW5kZW5jaWVzGAYgAygLMiEucXVpeG9zLnJ1bnRpbWUuRGVyaXZlZERlcGVuZGVuY3kSMAoNZmllbGRfZWRpdGluZxgHIAEoCzIZLnF1aXhvcy5vcmNoLkZpZWxkRWRpdGluZyJ3CgxGaWVsZEVkaXRpbmcSGwoTZ2V0dGVyX29wZXJhdGlvbl9pZBgBIAEoCRIbChNzZXR0ZXJfb3BlcmF0aW9uX2lkGAIgASgJEhUKDWRvY3VtZW50X3R5cGUYAyABKAkSFgoOYmluZGluZ19kaWdlc3QYBCABKAkizgEKGkVkaXRDYXBhYmlsaXR5RmllbGRSZXF1ZXN0EikKCmNhcGFiaWxpdHkYASABKAsyFS5xdWl4b3MuQ2FwYWJpbGl0eVJlZhIRCglvYmplY3RfaWQYAiABKAkSGwoTc2V0dGVyX29wZXJhdGlvbl9pZBgDIAEoCRIWCg5iaW5kaW5nX2RpZ2VzdBgEIAEoCRIhCgZ1cGRhdGUYBSABKAsyES5jYW1pbm8uQ3JkdFZhbHVlEhoKEmNsaWVudF9tdXRhdGlvbl9pZBgGIAEoCSLSAQoWV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdBIpCgpjYXBhYmlsaXR5GAEgASgLMhUucXVpeG9zLkNhcGFiaWxpdHlSZWYSEQoJb2JqZWN0X2lkGAIgASgJEj0KBWlucHV0GAMgAygLMi4ucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdC5JbnB1dEVudHJ5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASKVAgoUV2F0Y2hDYXBhYmlsaXR5RXZlbnQSFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIrCgphY3RpdmF0aW9uGAIgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbhIQCgh3YXRjaF9pZBgDIAEoCRIcCgV2YWx1ZRgEIAEoCzINLmNhbWluby5WYWx1ZRI3CgxkZXBlbmRlbmNpZXMYBSADKAsyIS5xdWl4b3MucnVudGltZS5EZXJpdmVkRGVwZW5kZW5jeRINCgVlcnJvchgGIAEoCRIPCgdpbml0aWFsGAcgASgIEjAKDWZpZWxkX2VkaXRpbmcYCCABKAsyGS5xdWl4b3Mub3JjaC5GaWVsZEVkaXRpbmciOgoTR2V0V29ya3NwYWNlUmVxdWVzdBIjChtpbmNsdWRlX2ludGVyZmFjZV9jb250cmFjdHMYASABKAgi6gIKFEdldFdvcmtzcGFjZVJlc3BvbnNlEhQKDHdvcmtzcGFjZV9pZBgBIAEoCRIdChV3b3Jrc3BhY2VfcmV2aXNpb25faWQYAiABKAkSGgoSc291cmNlX3Jvb3RfY29tbWl0GAMgASgJEioKImVtcHR5X2lucHV0X2NvbnN0cnVjdGlibGVfYXRvbV9pZHMYBCADKAkSPwoRY2FwYWJpbGl0eV9pbnB1dHMYBSADKAsyJC5xdWl4b3Mub3JjaC5DYXBhYmlsaXR5SW5wdXRDb250cmFjdBJBChJjb25zdHJ1Y3Rvcl9pbnB1dHMYBiADKAsyJS5xdWl4b3Mub3JjaC5Db25zdHJ1Y3RvcklucHV0Q29udHJhY3QSFwoPaW50ZXJmYWNlc19qc29uGAcgASgJEjgKEmNsYXNzX2NhcGFiaWxpdGllcxgIIAMoCzIcLnF1aXhvcy5vcmNoLkNsYXNzQ2FwYWJpbGl0eSK5AQoPQ2xhc3NDYXBhYmlsaXR5EhYKDmNvbmZvcm1hbmNlX2lkGAEgASgJEg8KB2F0b21faWQYAiABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAMgASgJEhUKDWRlZmluaXRpb25faWQYBCABKAkSFAoMb3BlcmF0aW9uX2lkGAUgASgJEhcKD2lucHV0X3R5cGVfanNvbhgGIAEoCRIYChBvdXRwdXRfdHlwZV9qc29uGAcgASgJImEKF0NhcGFiaWxpdHlJbnB1dENvbnRyYWN0Eh0KFWludGVyZmFjZV9yZXZpc2lvbl9pZBgBIAEoCRIUCgxvcGVyYXRpb25faWQYAiABKAkSEQoJdHlwZV9qc29uGAMgASgJIj4KGENvbnN0cnVjdG9ySW5wdXRDb250cmFjdBIPCgdhdG9tX2lkGAEgASgJEhEKCXR5cGVfanNvbhgCIAEoCSIYChZMaXN0QWN0aXZhdGlvbnNSZXF1ZXN0Ih8KHUxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXF1ZXN0IlAKHkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXNwb25zZRIuCgtkZXNjcmlwdG9ycxgBIAMoCzIZLnF1aXhvcy5QYWNrYWdlRGVzY3JpcHRvciIcChpMaXN0UGFja2FnZVJ1bnRpbWVzUmVxdWVzdCJSChtMaXN0UGFja2FnZVJ1bnRpbWVzUmVzcG9uc2USMwoIcnVudGltZXMYASADKAsyIS5xdWl4b3Mub3JjaC5QYWNrYWdlUnVudGltZVN0YXR1cyJHChdMaXN0QWN0aXZhdGlvbnNSZXNwb25zZRIsCgthY3RpdmF0aW9ucxgBIAMoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24iPwoWQ2xvc2VBY3RpdmF0aW9uUmVxdWVzdBIVCg1hY3RpdmF0aW9uX2lkGAEgASgJEg4KBnJlYXNvbhgCIAEoCSJGChdDbG9zZUFjdGl2YXRpb25SZXNwb25zZRIrCgphY3RpdmF0aW9uGAEgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbiLrAQoKQWN0aXZhdGlvbhIVCg1hY3RpdmF0aW9uX2lkGAEgASgJEigKBmV4cG9ydBgCIAEoCzIYLnF1aXhvcy5QYWNrYWdlRXhwb3J0UmVmEhEKCW9iamVjdF9pZBgDIAEoCRINCgVzdGF0ZRgEIAEoCRIOCgZkZW1hbmQYBSABKA0SEQoJb3BlbmVkX2F0GAYgASgJEhQKDGxhc3RfdXNlZF9hdBgHIAEoCRIYChBpZGxlX2RlYWRsaW5lX2F0GAggASgJEhEKCWNsb3NlZF9hdBgJIAEoCRIUCgxjbG9zZV9yZWFzb24YCiABKAkiswIKFFBhY2thZ2VSdW50aW1lU3RhdHVzEhMKC3J1bnRpbWVfa2V5GAEgASgJEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYAiABKAkSGQoRc291cmNlX3JlcG9zaXRvcnkYAyABKAkSFQoNc291cmNlX2NvbW1pdBgEIAEoCRIUCgxidWlsZF90YXJnZXQYBSABKAkSEwoLc2VydmVyX3BhdGgYBiABKAkSCwoDcGlkGAcgASgNEg0KBXN0YXRlGAggASgJEhIKCnN0YXJ0ZWRfYXQYCSABKAkSGQoRbGFzdF9oYW5kc2hha2VfYXQYCiABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGAsgASgJEh8KF2FkdmVydGlzZWRfZXhwb3J0X2NvdW50GAwgASgNMoAJChNPcmNoZXN0cmF0b3JSdW50aW1lEl8KEEludm9rZUNhcGFiaWxpdHkSJC5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVxdWVzdBolLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXNwb25zZRJlChNFZGl0Q2FwYWJpbGl0eUZpZWxkEicucXVpeG9zLm9yY2guRWRpdENhcGFiaWxpdHlGaWVsZFJlcXVlc3QaJS5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USaQoVSW52b2tlQ2xhc3NDYXBhYmlsaXR5EikucXVpeG9zLm9yY2guSW52b2tlQ2xhc3NDYXBhYmlsaXR5UmVxdWVzdBolLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXNwb25zZRJbCg9XYXRjaENhcGFiaWxpdHkSIy5xdWl4b3Mub3JjaC5XYXRjaENhcGFiaWxpdHlSZXF1ZXN0GiEucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5RXZlbnQwARJcCg9Db25zdHJ1Y3RPYmplY3QSIy5xdWl4b3Mub3JjaC5Db25zdHJ1Y3RPYmplY3RSZXF1ZXN0GiQucXVpeG9zLm9yY2guQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USjAEKH1Jlc29sdmVPckNvbnN0cnVjdFJlbGF0ZWRPYmplY3QSMy5xdWl4b3Mub3JjaC5SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBo0LnF1aXhvcy5vcmNoLlJlc29sdmVPckNvbnN0cnVjdFJlbGF0ZWRPYmplY3RSZXNwb25zZRJTCgxHZXRXb3Jrc3BhY2USIC5xdWl4b3Mub3JjaC5HZXRXb3Jrc3BhY2VSZXF1ZXN0GiEucXVpeG9zLm9yY2guR2V0V29ya3NwYWNlUmVzcG9uc2UScQoWTGlzdFBhY2thZ2VEZXNjcmlwdG9ycxIqLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXF1ZXN0GisucXVpeG9zLm9yY2guTGlzdFBhY2thZ2VEZXNjcmlwdG9yc1Jlc3BvbnNlEmgKE0xpc3RQYWNrYWdlUnVudGltZXMSJy5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZVJ1bnRpbWVzUmVxdWVzdBooLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlUnVudGltZXNSZXNwb25zZRJcCg9MaXN0QWN0aXZhdGlvbnMSIy5xdWl4b3Mub3JjaC5MaXN0QWN0aXZhdGlvbnNSZXF1ZXN0GiQucXVpeG9zLm9yY2guTGlzdEFjdGl2YXRpb25zUmVzcG9uc2USXAoPQ2xvc2VBY3RpdmF0aW9uEiMucXVpeG9zLm9yY2guQ2xvc2VBY3RpdmF0aW9uUmVxdWVzdBokLnF1aXhvcy5vcmNoLkNsb3NlQWN0aXZhdGlvblJlc3BvbnNlYgZwcm90bzM", [file_camino_api, file_quixos_package, file_quixos_refs, file_quixos_runtime]); /** * @generated from message quixos.orch.ConstructObjectRequest @@ -143,6 +143,33 @@ export type InvokeCapabilityRequest = Message<"quixos.orch.InvokeCapabilityReque export const InvokeCapabilityRequestSchema: GenMessage = /*@__PURE__*/ messageDesc(file_quixos_orch, 4); +/** + * @generated from message quixos.orch.InvokeClassCapabilityRequest + */ +export type InvokeClassCapabilityRequest = Message<"quixos.orch.InvokeClassCapabilityRequest"> & { + /** + * @generated from field: string conformance_id = 1; + */ + conformanceId: string; + + /** + * @generated from field: string operation_id = 2; + */ + operationId: string; + + /** + * @generated from field: map input = 3; + */ + input: { [key: string]: Value }; +}; + +/** + * Describes the message quixos.orch.InvokeClassCapabilityRequest. + * Use `create(InvokeClassCapabilityRequestSchema)` to create a new message. + */ +export const InvokeClassCapabilityRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_quixos_orch, 5); + /** * @generated from message quixos.orch.InvokeCapabilityResponse */ @@ -176,6 +203,11 @@ export type InvokeCapabilityResponse = Message<"quixos.orch.InvokeCapabilityResp * @generated from field: repeated quixos.runtime.DerivedDependency dependencies = 6; */ dependencies: DerivedDependency[]; + + /** + * @generated from field: quixos.orch.FieldEditing field_editing = 7; + */ + fieldEditing?: FieldEditing | undefined; }; /** @@ -183,7 +215,85 @@ export type InvokeCapabilityResponse = Message<"quixos.orch.InvokeCapabilityResp * Use `create(InvokeCapabilityResponseSchema)` to create a new message. */ export const InvokeCapabilityResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 5); + messageDesc(file_quixos_orch, 6); + +/** + * Resolved from the checked native getter/setter binding, not Value.source. + * + * @generated from message quixos.orch.FieldEditing + */ +export type FieldEditing = Message<"quixos.orch.FieldEditing"> & { + /** + * @generated from field: string getter_operation_id = 1; + */ + getterOperationId: string; + + /** + * @generated from field: string setter_operation_id = 2; + */ + setterOperationId: string; + + /** + * @generated from field: string document_type = 3; + */ + documentType: string; + + /** + * @generated from field: string binding_digest = 4; + */ + bindingDigest: string; +}; + +/** + * Describes the message quixos.orch.FieldEditing. + * Use `create(FieldEditingSchema)` to create a new message. + */ +export const FieldEditingSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_quixos_orch, 7); + +/** + * @generated from message quixos.orch.EditCapabilityFieldRequest + */ +export type EditCapabilityFieldRequest = Message<"quixos.orch.EditCapabilityFieldRequest"> & { + /** + * The public getter; setter must belong to the same value member. + * + * @generated from field: quixos.CapabilityRef capability = 1; + */ + capability?: CapabilityRef | undefined; + + /** + * @generated from field: string object_id = 2; + */ + objectId: string; + + /** + * @generated from field: string setter_operation_id = 3; + */ + setterOperationId: string; + + /** + * @generated from field: string binding_digest = 4; + */ + bindingDigest: string; + + /** + * @generated from field: camino.CrdtValue update = 5; + */ + update?: CrdtValue | undefined; + + /** + * @generated from field: string client_mutation_id = 6; + */ + clientMutationId: string; +}; + +/** + * Describes the message quixos.orch.EditCapabilityFieldRequest. + * Use `create(EditCapabilityFieldRequestSchema)` to create a new message. + */ +export const EditCapabilityFieldRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_quixos_orch, 8); /** * @generated from message quixos.orch.WatchCapabilityRequest @@ -210,7 +320,7 @@ export type WatchCapabilityRequest = Message<"quixos.orch.WatchCapabilityRequest * Use `create(WatchCapabilityRequestSchema)` to create a new message. */ export const WatchCapabilityRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 6); + messageDesc(file_quixos_orch, 9); /** * @generated from message quixos.orch.WatchCapabilityEvent @@ -250,6 +360,11 @@ export type WatchCapabilityEvent = Message<"quixos.orch.WatchCapabilityEvent"> & * @generated from field: bool initial = 7; */ initial: boolean; + + /** + * @generated from field: quixos.orch.FieldEditing field_editing = 8; + */ + fieldEditing?: FieldEditing | undefined; }; /** @@ -257,12 +372,18 @@ export type WatchCapabilityEvent = Message<"quixos.orch.WatchCapabilityEvent"> & * Use `create(WatchCapabilityEventSchema)` to create a new message. */ export const WatchCapabilityEventSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 7); + messageDesc(file_quixos_orch, 10); /** * @generated from message quixos.orch.GetWorkspaceRequest */ export type GetWorkspaceRequest = Message<"quixos.orch.GetWorkspaceRequest"> & { + /** + * Revision polling must not download the entire interface graph. + * + * @generated from field: bool include_interface_contracts = 1; + */ + includeInterfaceContracts: boolean; }; /** @@ -270,7 +391,7 @@ export type GetWorkspaceRequest = Message<"quixos.orch.GetWorkspaceRequest"> & { * Use `create(GetWorkspaceRequestSchema)` to create a new message. */ export const GetWorkspaceRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 8); + messageDesc(file_quixos_orch, 11); /** * @generated from message quixos.orch.GetWorkspaceResponse @@ -292,8 +413,8 @@ export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & sourceRootCommit: string; /** - * Checked constructors whose wire input can be empty. Web Studio intersects - * this with its temporary Createable marker; the marker is not a factory. + * Checked constructors whose wire input can be empty. The create panel uses + * class factory conformances instead of this constructor inventory. * * @generated from field: repeated string empty_input_constructible_atom_ids = 4; */ @@ -308,6 +429,18 @@ export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & * @generated from field: repeated quixos.orch.ConstructorInputContract constructor_inputs = 6; */ constructorInputs: ConstructorInputContract[]; + + /** + * Exact closed interface contracts used by checked presentation consumers. + * + * @generated from field: string interfaces_json = 7; + */ + interfacesJson: string; + + /** + * @generated from field: repeated quixos.orch.ClassCapability class_capabilities = 8; + */ + classCapabilities: ClassCapability[]; }; /** @@ -315,7 +448,54 @@ export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & * Use `create(GetWorkspaceResponseSchema)` to create a new message. */ export const GetWorkspaceResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 9); + messageDesc(file_quixos_orch, 12); + +/** + * @generated from message quixos.orch.ClassCapability + */ +export type ClassCapability = Message<"quixos.orch.ClassCapability"> & { + /** + * @generated from field: string conformance_id = 1; + */ + conformanceId: string; + + /** + * @generated from field: string atom_id = 2; + */ + atomId: string; + + /** + * @generated from field: string interface_revision_id = 3; + */ + interfaceRevisionId: string; + + /** + * @generated from field: string definition_id = 4; + */ + definitionId: string; + + /** + * @generated from field: string operation_id = 5; + */ + operationId: string; + + /** + * @generated from field: string input_type_json = 6; + */ + inputTypeJson: string; + + /** + * @generated from field: string output_type_json = 7; + */ + outputTypeJson: string; +}; + +/** + * Describes the message quixos.orch.ClassCapability. + * Use `create(ClassCapabilitySchema)` to create a new message. + */ +export const ClassCapabilitySchema: GenMessage = /*@__PURE__*/ + messageDesc(file_quixos_orch, 13); /** * @generated from message quixos.orch.CapabilityInputContract @@ -342,7 +522,7 @@ export type CapabilityInputContract = Message<"quixos.orch.CapabilityInputContra * Use `create(CapabilityInputContractSchema)` to create a new message. */ export const CapabilityInputContractSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 10); + messageDesc(file_quixos_orch, 14); /** * @generated from message quixos.orch.ConstructorInputContract @@ -364,7 +544,7 @@ export type ConstructorInputContract = Message<"quixos.orch.ConstructorInputCont * Use `create(ConstructorInputContractSchema)` to create a new message. */ export const ConstructorInputContractSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 11); + messageDesc(file_quixos_orch, 15); /** * @generated from message quixos.orch.ListActivationsRequest @@ -377,7 +557,7 @@ export type ListActivationsRequest = Message<"quixos.orch.ListActivationsRequest * Use `create(ListActivationsRequestSchema)` to create a new message. */ export const ListActivationsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 12); + messageDesc(file_quixos_orch, 16); /** * @generated from message quixos.orch.ListPackageDescriptorsRequest @@ -390,7 +570,7 @@ export type ListPackageDescriptorsRequest = Message<"quixos.orch.ListPackageDesc * Use `create(ListPackageDescriptorsRequestSchema)` to create a new message. */ export const ListPackageDescriptorsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 13); + messageDesc(file_quixos_orch, 17); /** * @generated from message quixos.orch.ListPackageDescriptorsResponse @@ -407,7 +587,7 @@ export type ListPackageDescriptorsResponse = Message<"quixos.orch.ListPackageDes * Use `create(ListPackageDescriptorsResponseSchema)` to create a new message. */ export const ListPackageDescriptorsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 14); + messageDesc(file_quixos_orch, 18); /** * @generated from message quixos.orch.ListPackageRuntimesRequest @@ -420,7 +600,7 @@ export type ListPackageRuntimesRequest = Message<"quixos.orch.ListPackageRuntime * Use `create(ListPackageRuntimesRequestSchema)` to create a new message. */ export const ListPackageRuntimesRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 15); + messageDesc(file_quixos_orch, 19); /** * @generated from message quixos.orch.ListPackageRuntimesResponse @@ -437,7 +617,7 @@ export type ListPackageRuntimesResponse = Message<"quixos.orch.ListPackageRuntim * Use `create(ListPackageRuntimesResponseSchema)` to create a new message. */ export const ListPackageRuntimesResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 16); + messageDesc(file_quixos_orch, 20); /** * @generated from message quixos.orch.ListActivationsResponse @@ -454,7 +634,7 @@ export type ListActivationsResponse = Message<"quixos.orch.ListActivationsRespon * Use `create(ListActivationsResponseSchema)` to create a new message. */ export const ListActivationsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 17); + messageDesc(file_quixos_orch, 21); /** * @generated from message quixos.orch.CloseActivationRequest @@ -476,7 +656,7 @@ export type CloseActivationRequest = Message<"quixos.orch.CloseActivationRequest * Use `create(CloseActivationRequestSchema)` to create a new message. */ export const CloseActivationRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 18); + messageDesc(file_quixos_orch, 22); /** * @generated from message quixos.orch.CloseActivationResponse @@ -493,7 +673,7 @@ export type CloseActivationResponse = Message<"quixos.orch.CloseActivationRespon * Use `create(CloseActivationResponseSchema)` to create a new message. */ export const CloseActivationResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 19); + messageDesc(file_quixos_orch, 23); /** * @generated from message quixos.orch.Activation @@ -555,7 +735,7 @@ export type Activation = Message<"quixos.orch.Activation"> & { * Use `create(ActivationSchema)` to create a new message. */ export const ActivationSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 20); + messageDesc(file_quixos_orch, 24); /** * @generated from message quixos.orch.PackageRuntimeStatus @@ -627,7 +807,7 @@ export type PackageRuntimeStatus = Message<"quixos.orch.PackageRuntimeStatus"> & * Use `create(PackageRuntimeStatusSchema)` to create a new message. */ export const PackageRuntimeStatusSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 21); + messageDesc(file_quixos_orch, 25); /** * @generated from service quixos.orch.OrchestratorRuntime @@ -641,6 +821,22 @@ export const OrchestratorRuntime: GenService<{ input: typeof InvokeCapabilityRequestSchema; output: typeof InvokeCapabilityResponseSchema; }, + /** + * @generated from rpc quixos.orch.OrchestratorRuntime.EditCapabilityField + */ + editCapabilityField: { + methodKind: "unary"; + input: typeof EditCapabilityFieldRequestSchema; + output: typeof InvokeCapabilityResponseSchema; + }, + /** + * @generated from rpc quixos.orch.OrchestratorRuntime.InvokeClassCapability + */ + invokeClassCapability: { + methodKind: "unary"; + input: typeof InvokeClassCapabilityRequestSchema; + output: typeof InvokeCapabilityResponseSchema; + }, /** * @generated from rpc quixos.orch.OrchestratorRuntime.WatchCapability */ diff --git a/src/references.ts b/src/references.ts index 66af0ff..65680a5 100644 --- a/src/references.ts +++ b/src/references.ts @@ -3,15 +3,26 @@ const identities = new WeakMap(); declare const referenceBrand: unique symbol; export interface QxObjectRef { - readonly [referenceBrand]: {readonly [K in Identity]: true}; + readonly [referenceBrand]: { readonly [K in Identity]: true }; equals(other: QxObjectRef): boolean; } class Reference { - constructor(id: string) { identities.set(this, id); Object.freeze(this); } - equals(other: unknown) { return isObjectReference(other) && identities.get(this) === identities.get(other); } - toJSON(): never { throw new Error("Object references cannot be serialized into ordinary data"); } - toString(): never { throw new Error("Object references cannot be coerced to strings"); } - [Symbol.toPrimitive](): never { throw new Error("Object references cannot be coerced to scalar values"); } + constructor(id: string) { + identities.set(this, id); + Object.freeze(this); + } + equals(other: unknown) { + return isObjectReference(other) && identities.get(this) === identities.get(other); + } + toJSON(): never { + throw new Error("Object references cannot be serialized into ordinary data"); + } + toString(): never { + throw new Error("Object references cannot be coerced to strings"); + } + [Symbol.toPrimitive](): never { + throw new Error("Object references cannot be coerced to scalar values"); + } } export const isObjectReference = (value: unknown): value is QxObjectRef => typeof value === "object" && value !== null && identities.has(value); @@ -27,7 +38,10 @@ export const referenceToWire = (value: unknown): string => { }; export const assertReferenceFree = (value: unknown, seen = new Set()): void => { if (!value || typeof value !== "object") return; - if (isObjectReference(value)) throw new Error("Managed references belong in declared RPC references or graph relationships, not ordinary state/messages"); + if (isObjectReference(value)) + throw new Error( + "Managed references belong in declared RPC references or graph relationships, not ordinary state/messages", + ); if (seen.has(value)) throw new Error("Cyclic ordinary data"); seen.add(value); if (!(value instanceof Uint8Array)) for (const child of Object.values(value)) assertReferenceFree(child, seen); diff --git a/src/relationships.ts b/src/relationships.ts index 79670bc..ae3a750 100644 --- a/src/relationships.ts +++ b/src/relationships.ts @@ -1,7 +1,10 @@ -import type {QxObjectRef} from "./references.js"; -import type {RelationshipCollection, RelationshipEntry} from "./index.js"; +import type { QxObjectRef } from "./references.js"; +import type { RelationshipCollection, RelationshipEntry } from "./index.js"; type Key = string | boolean | bigint; -type Port = {collection(): Promise>; replace(entries: RelationshipEntry[], expectedRevision: bigint): Promise>}; +type Port = { + collection(): Promise>; + replace(entries: RelationshipEntry[], expectedRevision: bigint): Promise>; +}; const checked = async (port: Port, revision: bigint) => { const snapshot = await port.collection(); if (snapshot.revision !== revision) throw new Error("STALE_COLLECTION_REVISION"); @@ -10,31 +13,39 @@ const checked = async (port: Port, revision: bigint) = /** Helpers never retry a failed CAS or silently overwrite concurrent edits. */ export const relationshipMap = (port: Port) => ({ read: () => port.collection(), - async get(key: K) {const snapshot = await port.collection(); return {revision: snapshot.revision, value: snapshot.entries.find((entry) => entry.key === key)?.target};}, + async get(key: K) { + const snapshot = await port.collection(); + return { revision: snapshot.revision, value: snapshot.entries.find((entry) => entry.key === key)?.target }; + }, async set(key: K, target: T, expectedRevision: bigint) { const snapshot = await checked(port, expectedRevision); const entries = snapshot.entries.filter((entry) => entry.key !== key); const existing = snapshot.entries.find((entry) => entry.key === key && entry.target.equals(target)); - entries.push(existing ?? {key, target}); + entries.push(existing ?? { key, target }); return port.replace(entries, expectedRevision); }, async delete(key: K, expectedRevision: bigint) { const snapshot = await checked(port, expectedRevision); - return port.replace(snapshot.entries.filter((entry) => entry.key !== key), expectedRevision); + return port.replace( + snapshot.entries.filter((entry) => entry.key !== key), + expectedRevision, + ); }, }); export const relationshipList = (port: Port) => ({ read: () => port.collection(), async insert(index: number, target: T, expectedRevision: bigint) { const snapshot = await checked(port, expectedRevision); - if (!Number.isSafeInteger(index) || index < 0 || index > snapshot.entries.length) throw new Error("List index out of bounds"); - snapshot.entries.splice(index, 0, {target}); + if (!Number.isSafeInteger(index) || index < 0 || index > snapshot.entries.length) + throw new Error("List index out of bounds"); + snapshot.entries.splice(index, 0, { target }); return port.replace(snapshot.entries, expectedRevision); }, async move(edgeId: string, index: number, expectedRevision: bigint) { const snapshot = await checked(port, expectedRevision); const prior = snapshot.entries.findIndex((entry) => entry.edgeId === edgeId); - if (prior < 0 || !Number.isSafeInteger(index) || index < 0 || index >= snapshot.entries.length) throw new Error("Unknown list entry or invalid index"); + if (prior < 0 || !Number.isSafeInteger(index) || index < 0 || index >= snapshot.entries.length) + throw new Error("Unknown list entry or invalid index"); const [entry] = snapshot.entries.splice(prior, 1); snapshot.entries.splice(index, 0, entry); return port.replace(snapshot.entries, expectedRevision); @@ -42,7 +53,10 @@ export const relationshipList = (port: Port) => ({ async delete(edgeId: string, expectedRevision: bigint) { const snapshot = await checked(port, expectedRevision); if (!snapshot.entries.some((entry) => entry.edgeId === edgeId)) throw new Error("Unknown list entry"); - return port.replace(snapshot.entries.filter((entry) => entry.edgeId !== edgeId), expectedRevision); + return port.replace( + snapshot.entries.filter((entry) => entry.edgeId !== edgeId), + expectedRevision, + ); }, }); export const relationshipSet = (port: Port) => ({ @@ -50,10 +64,13 @@ export const relationshipSet = (port: Port) => ({ async add(target: T, expectedRevision: bigint) { const snapshot = await checked(port, expectedRevision); if (snapshot.entries.some((entry) => entry.target.equals(target))) return snapshot; - return port.replace([...snapshot.entries, {target}], expectedRevision); + return port.replace([...snapshot.entries, { target }], expectedRevision); }, async delete(target: T, expectedRevision: bigint) { const snapshot = await checked(port, expectedRevision); - return port.replace(snapshot.entries.filter((entry) => !entry.target.equals(target)), expectedRevision); + return port.replace( + snapshot.entries.filter((entry) => !entry.target.equals(target)), + expectedRevision, + ); }, }); diff --git a/test/bindings.test.mjs b/test/bindings.test.mjs index a9f42d6..428cabe 100644 --- a/test/bindings.test.mjs +++ b/test/bindings.test.mjs @@ -1,108 +1,198 @@ import assert from "node:assert/strict"; import test from "node:test"; import { referenceFromWire } from "../dist/references.js"; -import { bindQxHandler, decodeQxValue, encodeQxValue, jsToProtoValue, liveValue, protoValueToJs, opaqueReactPropsBinding } from "../dist/index.js"; +import { + bindQxHandler, + decodeQxValue, + encodeQxValue, + jsToProtoValue, + liveValue, + protoValueToJs, +} from "../dist/index.js"; const scalar = (name) => ({ kind: "scalar", name }); const unit = { kind: "builtin", name: "unit" }; -test("opaque React props retain managed references while ordinary messages remain closed", () => { +test("free function handlers never acquire a receiver or object session", async () => { + const handler = bindQxHandler( + { receiver: "none", inputType: unit, outputType: unit, ports: {} }, + async (context) => { + assert.equal("objectId" in context, false); + assert.equal("openSession" in context, false); + }, + {}, + ); + await handler({ + inputProto: {}, + get objectId() { + throw new Error("no receiver exists"); + }, + openSession() { + assert.fail("no object session"); + }, + }); +}); +test("React props no longer have an opaque-message reference escape hatch", () => { const descriptorId = "org.quixos.web-studio.ReactProps"; const reference = referenceFromWire("obj:task"); - const type = {kind: "message", descriptorId}; - const messages = {[descriptorId]: opaqueReactPropsBinding}; - const decoded = decodeQxValue(type, encodeQxValue(type, {subject: reference, title: "Task"}, messages), messages); - assert.ok(decoded.subject.equals(reference)); - assert.equal(decoded.title, "Task"); - assert.throws(() => opaqueReactPropsBinding.encode(null), /object/); - assert.throws(() => opaqueReactPropsBinding.encode([]), /object/); - assert.throws(() => encodeQxValue({kind: "message", descriptorId: "Ordinary"}, {subject: reference}, - {Ordinary: opaqueReactPropsBinding}), /Managed references/); + const type = { kind: "message", descriptorId }; + const messages = { [descriptorId]: { encode: jsToProtoValue, decode: protoValueToJs } }; + assert.throws(() => encodeQxValue(type, { subject: reference }, messages), /Managed references/); + assert.throws(() => decodeQxValue(type, jsToProtoValue({ subject: reference }), messages), /Managed references/); }); test("declared record inputs preserve opaque references without opening message codecs", async () => { const target = referenceFromWire("obj:board"); - const type = {kind: "record", fields: { - target: {kind: "object-ref", expectation: {kind: "atom", atomId: "board"}}, - x: scalar("double"), note: {kind: "optional", value: scalar("string")}, - }}; - const encoded = encodeQxValue(type, {target, x: 12}, {}); + const type = { + kind: "record", + fields: { + target: { kind: "object-ref", expectation: { kind: "atom", atomId: "board" } }, + x: scalar("double"), + note: { kind: "optional", value: scalar("string") }, + }, + }; + const encoded = encodeQxValue(type, { target, x: 12 }, {}); const decoded = decodeQxValue(type, encoded, {}); assert.ok(decoded.target.equals(target)); assert.equal(decoded.x, 12); assert.equal(decoded.note, null); assert.throws(() => JSON.stringify(decoded), /cannot be serialized/); - assert.throws(() => encodeQxValue(type, {target, x: 12, hidden: target}, {}), /Unexpected/); - assert.throws(() => encodeQxValue(type, {x: 12}, {}), /Missing/); - assert.throws(() => encodeQxValue(type, {target: "obj:board", x: 12}, {}), /opaque object reference/); - const handler = bindQxHandler({inputType: type, outputType: unit, ports: {}}, async (context) => { - assert.ok(context.input.target.equals(target)); - assert.equal(context.input.x, 12); - }, {}); - await handler({objectId: target, inputProto: encoded.kind.value.fields}); + assert.throws(() => encodeQxValue(type, { target, x: 12, hidden: target }, {}), /Unexpected/); + assert.throws(() => encodeQxValue(type, { x: 12 }, {}), /Missing/); + assert.throws(() => encodeQxValue(type, { target: "obj:board", x: 12 }, {}), /opaque object reference/); + const handler = bindQxHandler( + { inputType: type, outputType: unit, ports: {} }, + async (context) => { + assert.ok(context.input.target.equals(target)); + assert.equal(context.input.x, 12); + }, + {}, + ); + await handler({ objectId: target, inputProto: encoded.kind.value.fields }); }); test("typed sessions rebind ports and cancellation to each acquired invocation", async () => { - const first = new AbortController(), second = new AbortController(); + const first = new AbortController(), + second = new AbortController(); let closed = false; - const context = (value, signal) => ({objectId: referenceFromWire("obj:owner"), inputProto: {}, signal, - state: () => ({live: async () => liveValue(jsToProtoValue(value))})}); - const raw = {...context(1n, first.signal), openSession: async () => ({id: "session:test", - run: (work) => work(context(2n, second.signal)), close: async () => {closed = true;}})}; - const handler = bindQxHandler({inputType: unit, outputType: unit, ports: {counter: {kind: "state", id: "counter", valueType: scalar("int64"), primitives: ["read"]}}}, async (bound) => { - assert.equal(bound.signal, first.signal); - const session = await bound.openSession(); - await session.run(async (next) => { - assert.equal(next.signal, second.signal); - assert.equal(await next.ports.counter.get(), 2n); - assert.equal(next.openSession, undefined); - }); - await session.close(); - }, {}); + const context = (value, signal) => ({ + objectId: referenceFromWire("obj:owner"), + inputProto: {}, + signal, + state: () => ({ live: async () => liveValue(jsToProtoValue(value)) }), + }); + const raw = { + ...context(1n, first.signal), + openSession: async () => ({ + id: "session:test", + run: (work) => work(context(2n, second.signal)), + close: async () => { + closed = true; + }, + }), + }; + const handler = bindQxHandler( + { + inputType: unit, + outputType: unit, + ports: { counter: { kind: "state", id: "counter", valueType: scalar("int64"), primitives: ["read"] } }, + }, + async (bound) => { + assert.equal(bound.signal, first.signal); + const session = await bound.openSession(); + await session.run(async (next) => { + assert.equal(next.signal, second.signal); + assert.equal(await next.ports.counter.get(), 2n); + assert.equal(next.openSession, undefined); + }); + await session.close(); + }, + {}, + ); await handler(raw); assert.equal(closed, true); }); test("binding codecs round trip nested bytes, 64-bit integers, nulls, and references", () => { - const values = [[scalar("int64"), -(2n ** 63n)], [scalar("uint64"), 2n ** 64n - 1n], + const values = [ + [scalar("int64"), -(2n ** 63n)], + [scalar("uint64"), 2n ** 64n - 1n], [scalar("bytes"), new Uint8Array([0, 255])], [{ kind: "list", value: { kind: "optional", value: scalar("int64") } }, [null, 2n ** 60n]], - [{ kind: "object-ref", expectation: { kind: "atom", atomId: "thing" } }, referenceFromWire("obj:thing")]]; + [{ kind: "object-ref", expectation: { kind: "atom", atomId: "thing" } }, referenceFromWire("obj:thing")], + ]; for (const [type, value] of values) assert.deepEqual(decodeQxValue(type, encodeQxValue(type, value, {}), {}), value); }); test("opaque references pass declared RPC boundaries but cannot enter ordinary data", () => { const reference = referenceFromWire("obj:thing"); - const type = {kind: "object-ref", expectation: {kind: "atom", atomId: "thing"}}; + const type = { kind: "object-ref", expectation: { kind: "atom", atomId: "thing" } }; const roundtrip = decodeQxValue(type, encodeQxValue(type, reference, {}), {}); assert.equal(reference.equals(roundtrip), true); assert.equal(reference.equals(referenceFromWire("obj:other")), false); - assert.throws(() => JSON.stringify({nested: [reference]}), /cannot be serialized/); + assert.throws(() => JSON.stringify({ nested: [reference] }), /cannot be serialized/); assert.throws(() => String(reference), /cannot be coerced/); assert.throws(() => encodeQxValue(type, "obj:thing", {}), /opaque/); assert.throws(() => encodeQxValue(scalar("string"), reference, {}), /Managed references/); - const message = {kind: "message", descriptorId: "Payload"}; - assert.throws(() => encodeQxValue(message, {nested: reference}, {Payload: {encode: jsToProtoValue}}), /Managed references/); - assert.throws(() => encodeQxValue(message, {}, {Payload: {encode: () => jsToProtoValue(reference)}}), /Managed references/); - assert.throws(() => decodeQxValue(message, jsToProtoValue(reference), {Payload: {decode: () => ({})}}), /Managed references/); + const message = { kind: "message", descriptorId: "Payload" }; + assert.throws( + () => encodeQxValue(message, { nested: reference }, { Payload: { encode: jsToProtoValue } }), + /Managed references/, + ); + assert.throws( + () => encodeQxValue(message, {}, { Payload: { encode: () => jsToProtoValue(reference) } }), + /Managed references/, + ); + assert.throws( + () => decodeQxValue(message, jsToProtoValue(reference), { Payload: { decode: () => ({}) } }), + /Managed references/, + ); }); test("typed state and interface ports preserve declared values and exact operation IDs", async () => { - const spec = { inputType: scalar("int64"), outputType: scalar("bytes"), ports: { - data: { kind: "state", id: "state-id", valueType: scalar("int64"), primitives: ["read", "write"] }, - reader: { kind: "interface", id: "interface-id", operations: { "payload.get": { id: "get-id", inputType: unit, outputType: scalar("bytes") } } }, - } }; + const spec = { + inputType: scalar("int64"), + outputType: scalar("bytes"), + ports: { + data: { kind: "state", id: "state-id", valueType: scalar("int64"), primitives: ["read", "write"] }, + reader: { + kind: "interface", + id: "interface-id", + operations: { "payload.get": { id: "get-id", inputType: unit, outputType: scalar("bytes") } }, + }, + }, + }; let written; - const handler = bindQxHandler(spec, async ({ input, ports }) => { - assert.equal(input, 2n ** 60n); - assert.equal(await ports.data.get(), 9n); - assert.deepEqual((await ports.data.live()).$quixosValue, jsToProtoValue(9n)); - assert.ok(ports.reader.objectId.equals(referenceFromWire("obj:reader"))); - assert.deepEqual((await ports.reader.live["payload.get"]()).$quixosValue, jsToProtoValue(new Uint8Array([7]))); - await ports.data.set(input); - return ports.reader["payload.get"](); - }, {}); - const result = await handler({ inputProto: { value: jsToProtoValue(2n ** 60n) }, objectId: "obj", - state: (id) => { assert.equal(id, "state-id"); return { - live: async () => liveValue(jsToProtoValue(9n)), set: async (value) => { written = value; }, - }; }, - interface: (id) => { assert.equal(id, "interface-id"); return { objectId: referenceFromWire("obj:reader"), live: async (operation, input) => { - assert.equal(operation, "get-id"); assert.deepEqual(input, {}); return liveValue(jsToProtoValue(new Uint8Array([7]))); - } }; }, + const handler = bindQxHandler( + spec, + async ({ input, ports }) => { + assert.equal(input, 2n ** 60n); + assert.equal(await ports.data.get(), 9n); + assert.deepEqual((await ports.data.live()).$quixosValue, jsToProtoValue(9n)); + assert.ok(ports.reader.objectId.equals(referenceFromWire("obj:reader"))); + assert.deepEqual((await ports.reader.live["payload.get"]()).$quixosValue, jsToProtoValue(new Uint8Array([7]))); + await ports.data.set(input); + return ports.reader["payload.get"](); + }, + {}, + ); + const result = await handler({ + inputProto: { value: jsToProtoValue(2n ** 60n) }, + objectId: "obj", + state: (id) => { + assert.equal(id, "state-id"); + return { + live: async () => liveValue(jsToProtoValue(9n)), + set: async (value) => { + written = value; + }, + }; + }, + interface: (id) => { + assert.equal(id, "interface-id"); + return { + objectId: referenceFromWire("obj:reader"), + live: async (operation, input) => { + assert.equal(operation, "get-id"); + assert.deepEqual(input, {}); + return liveValue(jsToProtoValue(new Uint8Array([7]))); + }, + }; + }, }); assert.equal(written.$quixosValue.kind.value, String(2n ** 60n)); assert.deepEqual(result.$quixosValue.kind.value, new Uint8Array([7])); @@ -110,8 +200,11 @@ test("typed state and interface ports preserve declared values and exact operati test("external message bindings and derived event types are used at the boundary", async () => { const message = { kind: "message", descriptorId: "Payload" }; const messages = { Payload: { encode: jsToProtoValue, decode: protoValueToJs } }; - const handler = bindQxHandler({ inputType: message, outputType: { kind: "builtin", name: "watch-handle" }, eventType: message, ports: {} }, - { kind: "derived", get: ({ input }) => ({ value: input.title }) }, messages); + const handler = bindQxHandler( + { inputType: message, outputType: { kind: "builtin", name: "watch-handle" }, eventType: message, ports: {} }, + { kind: "derived", get: ({ input }) => ({ value: input.title }) }, + messages, + ); assert.equal(handler.kind, "derived"); const result = await handler.get({ objectId: "obj", inputProto: { title: jsToProtoValue("hello") } }); assert.deepEqual(protoValueToJs(result.$quixosValue), { value: "hello" }); diff --git a/test/credential-file.test.mjs b/test/credential-file.test.mjs index 91dfc2e..6ce5658 100644 --- a/test/credential-file.test.mjs +++ b/test/credential-file.test.mjs @@ -1,30 +1,47 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import fs from 'node:fs/promises'; -import os from 'node:os'; -import path from 'node:path'; -import crypto from 'node:crypto'; -import {spawnSync} from 'node:child_process'; +import test from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import crypto from "node:crypto"; +import { spawnSync } from "node:child_process"; -test('supervised SDK reads a credential file and proves instance identity without an environment token', async t => { - const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'qx-sdk-credential-')); - t.after(() => fs.rm(directory, {recursive:true, force:true})); - const token = crypto.randomBytes(32).toString('hex'); - const filename = path.join(directory, 'process-token'); - await fs.writeFile(filename, token, {mode:0o600}); - const env = {...process.env, CAMINO_RUNTIME_AUTH_TOKEN_FILE: filename, - CAMINO_RUNTIME_AUTH_REQUIRED:'1', QUIXOS_RUNTIME_INSTANCE_ID:'instance:test'}; +test("supervised SDK reads a credential file and proves instance identity without an environment token", async (t) => { + const directory = await fs.mkdtemp(path.join(os.tmpdir(), "qx-sdk-credential-")); + t.after(() => fs.rm(directory, { recursive: true, force: true })); + const token = crypto.randomBytes(32).toString("hex"); + const filename = path.join(directory, "process-token"); + await fs.writeFile(filename, token, { mode: 0o600 }); + const env = { + ...process.env, + CAMINO_RUNTIME_AUTH_TOKEN_FILE: filename, + CAMINO_RUNTIME_AUTH_REQUIRED: "1", + QUIXOS_RUNTIME_INSTANCE_ID: "instance:test", + }; delete env.CAMINO_RUNTIME_AUTH_TOKEN; - const result = spawnSync(process.execPath, ['--input-type=module', '-e', ` - import {createPackageRuntimeRoutes} from ${JSON.stringify(new URL('../dist/index.js', import.meta.url).href)}; + const result = spawnSync( + process.execPath, + [ + "--input-type=module", + "-e", + ` + import {createPackageRuntimeRoutes} from ${JSON.stringify(new URL("../dist/index.js", import.meta.url).href)}; createPackageRuntimeRoutes({packageRevisionId:'package:test',exports:{}})({ service(_type, implementation) { console.log(JSON.stringify(implementation.handshake({nonce:'challenge'}))); } }); - `], {env, encoding:'utf8'}); + `, + ], + { env, encoding: "utf8" }, + ); assert.equal(result.status, 0, result.stderr); const handshake = JSON.parse(result.stdout); - assert.equal(handshake.authenticationProof, crypto.createHmac('sha256', token) - .update(JSON.stringify(['challenge','instance:test','package:test'])).digest('hex')); - assert.ok(handshake.capabilities.includes('epoch-grants-v1')); + assert.equal( + handshake.authenticationProof, + crypto + .createHmac("sha256", token) + .update(JSON.stringify(["challenge", "instance:test", "package:test"])) + .digest("hex"), + ); + assert.ok(handshake.capabilities.includes("epoch-grants-v1")); assert.ok(!result.stdout.includes(token)); }); diff --git a/test/migration.test.mjs b/test/migration.test.mjs index 39e0a83..26e61c7 100644 --- a/test/migration.test.mjs +++ b/test/migration.test.mjs @@ -1,13 +1,25 @@ import test from "node:test"; import assert from "node:assert/strict"; -import {createMigrationContext, migrationObjectId} from "../dist/migration.js"; +import { createMigrationContext, migrationObjectId } from "../dist/migration.js"; test("migration context offers restricted ports and deterministic helper identities", () => { - const input = {schemaVersion: 1, executionId: "operation:transition", exportId: "migrate", ports: [ - {name: "old", binding: "slot:name", view: "old", access: ["read"], states: [{objectId: "obj:one", value: "Alice"}]}, - {name: "new", binding: "slot:name", view: "new", access: ["write"]}, {name: "newRead", binding: "slot:name", view: "new", access: ["read"]}, - {name: "helpers", binding: "atom:helper", view: "new", access: ["create"]}, - ]}; - const {context, result} = createMigrationContext(input); + const input = { + schemaVersion: 1, + executionId: "operation:transition", + exportId: "migrate", + ports: [ + { + name: "old", + binding: "slot:name", + view: "old", + access: ["read"], + states: [{ objectId: "obj:one", value: "Alice" }], + }, + { name: "new", binding: "slot:name", view: "new", access: ["write"] }, + { name: "newRead", binding: "slot:name", view: "new", access: ["read"] }, + { name: "helpers", binding: "atom:helper", view: "new", access: ["create"] }, + ], + }; + const { context, result } = createMigrationContext(input); assert.equal(context.read("old", "obj:one"), "Alice"); assert.throws(() => context.write("old", "obj:one", "Bob"), /does not grant/); assert.throws(() => context.read("new", "obj:one"), /does not grant/); @@ -20,5 +32,5 @@ test("migration context offers restricted ports and deterministic helper identit assert.equal(helper, migrationObjectId(input.executionId, "helpers", "one")); assert.equal(context.create("helpers", "one"), helper); assert.equal(result().creates.length, 1); - assert.deepEqual(input.ports[0].states, [{objectId: "obj:one", value: "Alice"}]); + assert.deepEqual(input.ports[0].states, [{ objectId: "obj:one", value: "Alice" }]); }); diff --git a/test/relationships.test.mjs b/test/relationships.test.mjs index 87e0739..49d894d 100644 --- a/test/relationships.test.mjs +++ b/test/relationships.test.mjs @@ -1,16 +1,23 @@ import test from "node:test"; import assert from "node:assert/strict"; -import {relationshipMap, relationshipList, relationshipSet} from "../dist/index.js"; -import {referenceFromWire} from "../dist/references.js"; +import { relationshipMap, relationshipList, relationshipSet } from "../dist/index.js"; +import { referenceFromWire } from "../dist/references.js"; test("relationship helpers preserve handles and require explicit collection revisions", async () => { - let current = {revision: 0n, entries: []}, serial = 0; - const port = {collection: async () => ({revision: current.revision, entries: current.entries.map((entry) => ({...entry}))}), + let current = { revision: 0n, entries: [] }, + serial = 0; + const port = { + collection: async () => ({ revision: current.revision, entries: current.entries.map((entry) => ({ ...entry })) }), replace: async (entries, revision) => { assert.equal(revision, current.revision); - current = {revision: revision + 1n, entries: entries.map((entry) => ({...entry, edgeId: entry.edgeId ?? `edge:${++serial}`}))}; + current = { + revision: revision + 1n, + entries: entries.map((entry) => ({ ...entry, edgeId: entry.edgeId ?? `edge:${++serial}` })), + }; return port.collection(); - }}; - const a = referenceFromWire("obj:a"), b = referenceFromWire("obj:b"); + }, + }; + const a = referenceFromWire("obj:a"), + b = referenceFromWire("obj:b"); const map = relationshipMap(port); await map.set("a1", a, 0n); assert.equal((await map.get("a1")).value.equals(a), true); diff --git a/test/runtime.test.mjs b/test/runtime.test.mjs index 39bea4b..6a7678f 100644 --- a/test/runtime.test.mjs +++ b/test/runtime.test.mjs @@ -21,11 +21,7 @@ import { ValueSchema, ValueSourceSchema, } from "../dist/camino/api_pb.js"; -import { - EdgeDependencySchema, - InjectedDependencySchema, - PackageExportRefSchema, -} from "../dist/quixos/refs_pb.js"; +import { EdgeDependencySchema, InjectedDependencySchema, PackageExportRefSchema } from "../dist/quixos/refs_pb.js"; import { InvokeCapabilityResponseSchema, OrchestratorRuntime } from "../dist/quixos/orch_pb.js"; import { DerivedDependencySchema, PackageRuntime } from "../dist/quixos/runtime_pb.js"; @@ -93,26 +89,30 @@ test("runtime context exposes only explicitly injected ports", async () => { resolveEdge: async () => ({ edges: [] }), connectEdge: async () => ({}), }; - const context = createRuntimeContext(camino, {}, { - objectId: "obj:task", - input: {}, - dependencies: [ - create(InjectedDependencySchema, { - portId: "port:title", - binding: { case: "stateSlotId", value: "slot:task:title" }, - }), - create(InjectedDependencySchema, { - portId: "port:children", - binding: { - case: "edge", - value: create(EdgeDependencySchema, { - edgeTypeId: "edge:task:children", - projectionId: "projection:task:children", - }), - }, - }), - ], - }); + const context = createRuntimeContext( + camino, + {}, + { + objectId: "obj:task", + input: {}, + dependencies: [ + create(InjectedDependencySchema, { + portId: "port:title", + binding: { case: "stateSlotId", value: "slot:task:title" }, + }), + create(InjectedDependencySchema, { + portId: "port:children", + binding: { + case: "edge", + value: create(EdgeDependencySchema, { + edgeTypeId: "edge:task:children", + projectionId: "projection:task:children", + }), + }, + }), + ], + }, + ); assert.equal(await context.state("port:title").get(), "obj:task/slot:task:title"); await context.state("port:title").set("Changed"); @@ -126,23 +126,30 @@ test("runtime context exposes only explicitly injected ports", async () => { test("injected ports preserve an explicitly traversed object through PackageRuntime RPC", async () => { const reads = []; - const caminoServer = await listen((router) => router.service(CaminoService, { - readState: (request) => { - reads.push(request); - return { value: jsToProtoValue("Project name") }; - }, - })); - const runtimeServer = await listen(createPackageRuntimeRoutes({ - packageRevisionId: "package:test@1", - caminoUrl: caminoServer.url, - exports: { - "export:test:value": (context) => context.state("port:value").get(), - }, - })); - const client = createClient(PackageRuntime, createConnectTransport({ - baseUrl: runtimeServer.url, - httpVersion: "1.1", - })); + const caminoServer = await listen((router) => + router.service(CaminoService, { + readState: (request) => { + reads.push(request); + return { value: jsToProtoValue("Project name") }; + }, + }), + ); + const runtimeServer = await listen( + createPackageRuntimeRoutes({ + packageRevisionId: "package:test@1", + caminoUrl: caminoServer.url, + exports: { + "export:test:value": (context) => context.state("port:value").get(), + }, + }), + ); + const client = createClient( + PackageRuntime, + createConnectTransport({ + baseUrl: runtimeServer.url, + httpVersion: "1.1", + }), + ); try { const response = await client.invoke({ export: create(PackageExportRefSchema, { @@ -150,11 +157,13 @@ test("injected ports preserve an explicitly traversed object through PackageRunt exportId: "export:test:value", }), objectId: "obj:component", - dependencies: [create(InjectedDependencySchema, { - portId: "port:value", - objectId: "obj:project", - binding: { case: "stateSlotId", value: "slot:project:name" }, - })], + dependencies: [ + create(InjectedDependencySchema, { + portId: "port:value", + objectId: "obj:project", + binding: { case: "stateSlotId", value: "slot:project:name" }, + }), + ], }); assert.equal(response.ok, true); assert.equal(protoValueToJs(response.result), "Project name"); @@ -178,32 +187,40 @@ test("interface views invoke a related object and propagate transitive live depe }), }), }); - const orchServer = await listen((router) => router.service(OrchestratorRuntime, { - invokeCapability: (request) => { - invocations.push(request); - return create(InvokeCapabilityResponseSchema, { - ok: true, - result: sourceValue, - dependencies: [create(DerivedDependencySchema, { - kind: "state", - objectId: "obj:project", - attachmentId: "slot:project:name", - })], - }); - }, - })); - const runtimeServer = await listen(createPackageRuntimeRoutes({ - packageRevisionId: "package:test@1", - orchUrl: orchServer.url, - exports: { - "export:test:value": derived((context) => - context.interface("port:named").live("operation:named:name:get")), - }, - })); - const client = createClient(PackageRuntime, createConnectTransport({ - baseUrl: runtimeServer.url, - httpVersion: "1.1", - })); + const orchServer = await listen((router) => + router.service(OrchestratorRuntime, { + invokeCapability: (request) => { + invocations.push(request); + return create(InvokeCapabilityResponseSchema, { + ok: true, + result: sourceValue, + dependencies: [ + create(DerivedDependencySchema, { + kind: "state", + objectId: "obj:project", + attachmentId: "slot:project:name", + }), + ], + }); + }, + }), + ); + const runtimeServer = await listen( + createPackageRuntimeRoutes({ + packageRevisionId: "package:test@1", + orchUrl: orchServer.url, + exports: { + "export:test:value": derived((context) => context.interface("port:named").live("operation:named:name:get")), + }, + }), + ); + const client = createClient( + PackageRuntime, + createConnectTransport({ + baseUrl: runtimeServer.url, + httpVersion: "1.1", + }), + ); try { const response = await client.invoke({ export: create(PackageExportRefSchema, { @@ -211,11 +228,13 @@ test("interface views invoke a related object and propagate transitive live depe exportId: "export:test:value", }), objectId: "obj:component", - dependencies: [create(InjectedDependencySchema, { - portId: "port:named", - objectId: "obj:project", - binding: { case: "interfaceRevisionId", value: "interface:named@1" }, - })], + dependencies: [ + create(InjectedDependencySchema, { + portId: "port:named", + objectId: "obj:project", + binding: { case: "interfaceRevisionId", value: "interface:named@1" }, + }), + ], }); assert.equal(response.ok, true); assert.equal(invocations[0]?.objectId, "obj:project"); @@ -232,64 +251,80 @@ test("interface views invoke a related object and propagate transitive live depe test("derived interface views reread after their transitive subscriptions become live", async () => { let current = "before subscription"; let invocationCount = 0; - const caminoServer = await listen((router) => router.service(CaminoService, { - watchObject: async function* (request, context) { - // Model a write racing the first nested capability read. Camino makes - // the subscription live before yielding this snapshot. - current = "after subscription"; - yield { - objectId: request.objectId, - snapshot: create(CaminoObjectSchema, { - id: request.objectId, - atomId: "atom:project", - workspaceRevisionId: "workspace:test@1", - }), - }; - await new Promise((resolve) => - context.signal.addEventListener("abort", resolve, { once: true })); - }, - })); - const orchServer = await listen((router) => router.service(OrchestratorRuntime, { - invokeCapability: () => { - invocationCount += 1; - return create(InvokeCapabilityResponseSchema, { - ok: true, - result: jsToProtoValue(current), - dependencies: [create(DerivedDependencySchema, { - kind: "state", - objectId: "obj:project", - attachmentId: "slot:project:name", - })], - }); - }, - })); - const runtimeServer = await listen(createPackageRuntimeRoutes({ - packageRevisionId: "package:test@1", - caminoUrl: caminoServer.url, - orchUrl: orchServer.url, - exports: { - "export:test:value": derived((context) => - context.interface("port:named").invoke("operation:named:name:get")), - }, - })); - const client = createClient(PackageRuntime, createConnectTransport({ - baseUrl: runtimeServer.url, - httpVersion: "1.1", - })); + const caminoServer = await listen((router) => + router.service(CaminoService, { + watchObject: async function* (request, context) { + // Model a write racing the first nested capability read. Camino makes + // the subscription live before yielding this snapshot. + current = "after subscription"; + yield { + objectId: request.objectId, + snapshot: create(CaminoObjectSchema, { + id: request.objectId, + atomId: "atom:project", + workspaceRevisionId: "workspace:test@1", + }), + }; + await new Promise((resolve) => context.signal.addEventListener("abort", resolve, { once: true })); + }, + }), + ); + const orchServer = await listen((router) => + router.service(OrchestratorRuntime, { + invokeCapability: () => { + invocationCount += 1; + return create(InvokeCapabilityResponseSchema, { + ok: true, + result: jsToProtoValue(current), + dependencies: [ + create(DerivedDependencySchema, { + kind: "state", + objectId: "obj:project", + attachmentId: "slot:project:name", + }), + ], + }); + }, + }), + ); + const runtimeServer = await listen( + createPackageRuntimeRoutes({ + packageRevisionId: "package:test@1", + caminoUrl: caminoServer.url, + orchUrl: orchServer.url, + exports: { + "export:test:value": derived((context) => context.interface("port:named").invoke("operation:named:name:get")), + }, + }), + ); + const client = createClient( + PackageRuntime, + createConnectTransport({ + baseUrl: runtimeServer.url, + httpVersion: "1.1", + }), + ); const controller = new AbortController(); try { - const stream = client.watch({ - export: create(PackageExportRefSchema, { - packageRevisionId: "package:test@1", - exportId: "export:test:value", - }), - objectId: "obj:component", - dependencies: [create(InjectedDependencySchema, { - portId: "port:named", - objectId: "obj:project", - binding: { case: "interfaceRevisionId", value: "interface:named@1" }, - })], - }, { signal: controller.signal })[Symbol.asyncIterator](); + const stream = client + .watch( + { + export: create(PackageExportRefSchema, { + packageRevisionId: "package:test@1", + exportId: "export:test:value", + }), + objectId: "obj:component", + dependencies: [ + create(InjectedDependencySchema, { + portId: "port:named", + objectId: "obj:project", + binding: { case: "interfaceRevisionId", value: "interface:named@1" }, + }), + ], + }, + { signal: controller.signal }, + ) + [Symbol.asyncIterator](); const initial = await stream.next(); assert.equal(protoValueToJs(initial.value?.value), "after subscription"); assert.equal(invocationCount, 2); @@ -305,56 +340,70 @@ test("derived watches subscribe before reading and emit only real changes", asyn let current = "before"; let subscribed = false; const changes = []; - const caminoServer = await listen((router) => router.service(CaminoService, { - readState: () => { - assert.equal(subscribed, true, "the dependency stream must be live before the state read"); - return { value: jsToProtoValue(current) }; - }, - watchObject: async function* (request, context) { - subscribed = true; - yield { - objectId: request.objectId, - snapshot: create(CaminoObjectSchema, { - id: request.objectId, - atomId: "atom:test", - workspaceRevisionId: "workspace:test@1", - }), - }; - while (!context.signal.aborted) { - const changed = await new Promise((resolve) => { - const finish = () => resolve(true); - changes.push(finish); - context.signal.addEventListener("abort", () => resolve(false), { once: true }); - }); - if (!changed) return; - yield { objectId: request.objectId }; - } - }, - })); - const runtimeServer = await listen(createPackageRuntimeRoutes({ - packageRevisionId: "package:test@1", - caminoUrl: caminoServer.url, - exports: { - "export:test:value": derived((context) => context.state("port:value").get()), - }, - })); - const client = createClient(PackageRuntime, createConnectTransport({ - baseUrl: runtimeServer.url, - httpVersion: "1.1", - })); + const caminoServer = await listen((router) => + router.service(CaminoService, { + readState: () => { + assert.equal(subscribed, true, "the dependency stream must be live before the state read"); + return { value: jsToProtoValue(current) }; + }, + watchObject: async function* (request, context) { + subscribed = true; + yield { + objectId: request.objectId, + snapshot: create(CaminoObjectSchema, { + id: request.objectId, + atomId: "atom:test", + workspaceRevisionId: "workspace:test@1", + }), + }; + while (!context.signal.aborted) { + const changed = await new Promise((resolve) => { + const finish = () => resolve(true); + changes.push(finish); + context.signal.addEventListener("abort", () => resolve(false), { once: true }); + }); + if (!changed) return; + yield { objectId: request.objectId }; + } + }, + }), + ); + const runtimeServer = await listen( + createPackageRuntimeRoutes({ + packageRevisionId: "package:test@1", + caminoUrl: caminoServer.url, + exports: { + "export:test:value": derived((context) => context.state("port:value").get()), + }, + }), + ); + const client = createClient( + PackageRuntime, + createConnectTransport({ + baseUrl: runtimeServer.url, + httpVersion: "1.1", + }), + ); const controller = new AbortController(); try { - const stream = client.watch({ - export: create(PackageExportRefSchema, { - packageRevisionId: "package:test@1", - exportId: "export:test:value", - }), - objectId: "obj:test", - dependencies: [create(InjectedDependencySchema, { - portId: "port:value", - binding: { case: "stateSlotId", value: "slot:test:value" }, - })], - }, { signal: controller.signal })[Symbol.asyncIterator](); + const stream = client + .watch( + { + export: create(PackageExportRefSchema, { + packageRevisionId: "package:test@1", + exportId: "export:test:value", + }), + objectId: "obj:test", + dependencies: [ + create(InjectedDependencySchema, { + portId: "port:value", + binding: { case: "stateSlotId", value: "slot:test:value" }, + }), + ], + }, + { signal: controller.signal }, + ) + [Symbol.asyncIterator](); const initial = await stream.next(); assert.equal(protoValueToJs(initial.value?.value), "before"); assert.equal(initial.value?.initial, true);