@oomol-lab/open-flow 0.1.0-beta.80 → 0.1.0-beta.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15393,6 +15393,21 @@ function applyFlowChanges(content, operations) {
15393
15393
  });
15394
15394
  }
15395
15395
  //#endregion
15396
+ //#region src/connector/common/actionContract.ts
15397
+ /** These rules also apply to existing Revision snapshots without a catalog lookup. */
15398
+ var connectorActionContracts = { "leina.send_notification": {
15399
+ inputConstraints: [{
15400
+ handle: "leinaId",
15401
+ jsonSchema: {
15402
+ type: "string",
15403
+ pattern: "\\S"
15404
+ },
15405
+ code: "leina.robot-required",
15406
+ message: "Select a Leina robot or bind its identifier to an input source."
15407
+ }],
15408
+ pendingMessageInput: "message"
15409
+ } };
15410
+ //#endregion
15396
15411
  //#region src/flow/common/edgeChanges.ts
15397
15412
  function connect(content, edge) {
15398
15413
  const selected = content.document.graph;
@@ -15403,11 +15418,12 @@ function connect(content, edge) {
15403
15418
  }];
15404
15419
  const source = selected.nodes[edge.source];
15405
15420
  const target = selected.nodes[edge.target];
15406
- if (edge.sourceHandle == "pending" && (source?.kind == "approval" || source?.kind == "wait") && target?.kind == "task" && "executor" in target.task && target.task.executor.kind == "connector" && target.task.executor.action == "leina.send_notification" && (target.inputs.message == null || target.inputs.message.kind == "unset")) changes.push({
15421
+ const messageInput = target?.kind == "task" && "executor" in target.task && target.task.executor.kind == "connector" ? connectorActionContracts[target.task.executor.action]?.pendingMessageInput : void 0;
15422
+ if (edge.sourceHandle == "pending" && (source?.kind == "approval" || source?.kind == "wait") && target?.kind == "task" && messageInput != null && (target.inputs[messageInput] == null || target.inputs[messageInput]?.kind == "unset")) changes.push({
15407
15423
  kind: "graph.node.input.set",
15408
15424
  nodeId: edge.target,
15409
- handle: "message",
15410
- before: target.inputs.message,
15425
+ handle: messageInput,
15426
+ before: target.inputs[messageInput],
15411
15427
  value: {
15412
15428
  kind: "sources",
15413
15429
  sources: [{
@@ -57,6 +57,21 @@ function dequal(foo, bar) {
57
57
  return foo !== foo && bar !== bar;
58
58
  }
59
59
  //#endregion
60
+ //#region src/connector/common/actionContract.ts
61
+ /** These rules also apply to existing Revision snapshots without a catalog lookup. */
62
+ var connectorActionContracts = { "leina.send_notification": {
63
+ inputConstraints: [{
64
+ handle: "leinaId",
65
+ jsonSchema: {
66
+ type: "string",
67
+ pattern: "\\S"
68
+ },
69
+ code: "leina.robot-required",
70
+ message: "Select a Leina robot or bind its identifier to an input source."
71
+ }],
72
+ pendingMessageInput: "message"
73
+ } };
74
+ //#endregion
60
75
  //#region src/flow/common/edgeChanges.ts
61
76
  function connect(content, edge) {
62
77
  const selected = content.document.graph;
@@ -67,11 +82,12 @@ function connect(content, edge) {
67
82
  }];
68
83
  const source = selected.nodes[edge.source];
69
84
  const target = selected.nodes[edge.target];
70
- if (edge.sourceHandle == "pending" && (source?.kind == "approval" || source?.kind == "wait") && target?.kind == "task" && "executor" in target.task && target.task.executor.kind == "connector" && target.task.executor.action == "leina.send_notification" && (target.inputs.message == null || target.inputs.message.kind == "unset")) changes.push({
85
+ const messageInput = target?.kind == "task" && "executor" in target.task && target.task.executor.kind == "connector" ? connectorActionContracts[target.task.executor.action]?.pendingMessageInput : void 0;
86
+ if (edge.sourceHandle == "pending" && (source?.kind == "approval" || source?.kind == "wait") && target?.kind == "task" && messageInput != null && (target.inputs[messageInput] == null || target.inputs[messageInput]?.kind == "unset")) changes.push({
71
87
  kind: "graph.node.input.set",
72
88
  nodeId: edge.target,
73
- handle: "message",
74
- before: target.inputs.message,
89
+ handle: messageInput,
90
+ before: target.inputs[messageInput],
75
91
  value: {
76
92
  kind: "sources",
77
93
  sources: [{
@@ -22,6 +22,21 @@ function dequal(foo, bar) {
22
22
  return foo !== foo && bar !== bar;
23
23
  }
24
24
  //#endregion
25
+ //#region src/connector/common/actionContract.ts
26
+ /** These rules also apply to existing Revision snapshots without a catalog lookup. */
27
+ var connectorActionContracts = { "leina.send_notification": {
28
+ inputConstraints: [{
29
+ handle: "leinaId",
30
+ jsonSchema: {
31
+ type: "string",
32
+ pattern: "\\S"
33
+ },
34
+ code: "leina.robot-required",
35
+ message: "Select a Leina robot or bind its identifier to an input source."
36
+ }],
37
+ pendingMessageInput: "message"
38
+ } };
39
+ //#endregion
25
40
  //#region ../../node_modules/.bun/@cfworker+json-schema@4.1.1/node_modules/@cfworker/json-schema/dist/esm/deep-compare-strict.js
26
41
  function deepCompareStrict(a, b) {
27
42
  const typeofa = typeof a;
@@ -25089,15 +25104,17 @@ async function validateFlow(revision, engine) {
25089
25104
  message,
25090
25105
  path: `/document/graph/nodes/${nodeId}/task/executor`
25091
25106
  });
25092
- if (task.executor.kind == "connector" && task.executor.action == "leina.send_notification") {
25093
- const mapping = node.inputs.leinaId;
25094
- const value = inputValue(mapping, nodeInputPorts(node).leinaId?.value);
25095
- if ((mapping?.kind != "sources" || mapping.sources.length == 0) && (typeof value != "string" || value.trim().length == 0)) checked.diagnostics.push({
25096
- code: "leina.robot-required",
25107
+ if (task.executor.kind == "connector") for (const constraint of connectorActionContracts[task.executor.action]?.inputConstraints ?? []) {
25108
+ const mapping = node.inputs[constraint.handle];
25109
+ if (mapping?.kind == "sources" && mapping.sources.length > 0) continue;
25110
+ const value = inputValue(mapping, nodeInputPorts(node)[constraint.handle]?.value);
25111
+ if (value !== void 0 && matchesSchema(value, constraint.jsonSchema)) continue;
25112
+ checked.diagnostics.push({
25113
+ code: constraint.code,
25097
25114
  column: 0,
25098
25115
  line: 1,
25099
- message: "Select a Leina robot or bind its identifier to an input source.",
25100
- path: `/document/graph/nodes/${nodeId}/inputs/leinaId`
25116
+ message: constraint.message,
25117
+ path: `/document/graph/nodes/${nodeId}/inputs/${constraint.handle}`
25101
25118
  });
25102
25119
  }
25103
25120
  if (task.executor.kind == "connector" && task.executor.action.length == 0) checked.diagnostics.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oomol-lab/open-flow",
3
- "version": "0.1.0-beta.80",
3
+ "version": "0.1.0-beta.81",
4
4
  "description": "TypeScript contracts and Browser runtime for Open Flow.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {
@@ -0,0 +1,26 @@
1
+ import type { JsonValue } from '../../types/index.ts'
2
+
3
+ interface ConnectorActionContract {
4
+ readonly inputConstraints?: readonly {
5
+ readonly handle: string
6
+ readonly jsonSchema: JsonValue
7
+ readonly code: string
8
+ readonly message: string
9
+ }[]
10
+ readonly pendingMessageInput?: string
11
+ }
12
+
13
+ /** These rules also apply to existing Revision snapshots without a catalog lookup. */
14
+ export const connectorActionContracts: Readonly<Record<string, ConnectorActionContract | undefined>> = {
15
+ 'leina.send_notification': {
16
+ inputConstraints: [
17
+ {
18
+ handle: 'leinaId',
19
+ jsonSchema: { type: 'string', pattern: '\\S' },
20
+ code: 'leina.robot-required',
21
+ message: 'Select a Leina robot or bind its identifier to an input source.',
22
+ },
23
+ ],
24
+ pendingMessageInput: 'message',
25
+ },
26
+ }
@@ -1,6 +1,7 @@
1
1
  import type { ChangeOperation, GraphEdge, RevisionContent } from './change.ts'
2
2
 
3
3
  import { dequal } from 'dequal/lite'
4
+ import { connectorActionContracts } from '../../connector/common/actionContract.ts'
4
5
 
5
6
  export function connect(content: RevisionContent, edge: GraphEdge): readonly ChangeOperation[] {
6
7
  const selected = content.document.graph
@@ -8,20 +9,22 @@ export function connect(content: RevisionContent, edge: GraphEdge): readonly Cha
8
9
  const changes: ChangeOperation[] = [{ edge, kind: 'graph.edge.connect' }]
9
10
  const source = selected.nodes[edge.source]
10
11
  const target = selected.nodes[edge.target]
12
+ const messageInput =
13
+ target?.kind == 'task' && 'executor' in target.task && target.task.executor.kind == 'connector'
14
+ ? connectorActionContracts[target.task.executor.action]?.pendingMessageInput
15
+ : undefined
11
16
  if (
12
17
  edge.sourceHandle == 'pending' &&
13
18
  (source?.kind == 'approval' || source?.kind == 'wait') &&
14
19
  target?.kind == 'task' &&
15
- 'executor' in target.task &&
16
- target.task.executor.kind == 'connector' &&
17
- target.task.executor.action == 'leina.send_notification' &&
18
- (target.inputs.message == null || target.inputs.message.kind == 'unset')
20
+ messageInput != null &&
21
+ (target.inputs[messageInput] == null || target.inputs[messageInput]?.kind == 'unset')
19
22
  ) {
20
23
  changes.push({
21
24
  kind: 'graph.node.input.set',
22
25
  nodeId: edge.target,
23
- handle: 'message',
24
- before: target.inputs.message,
26
+ handle: messageInput,
27
+ before: target.inputs[messageInput],
25
28
  value: { kind: 'sources', sources: [{ kind: 'node', nodeId: edge.source, output: 'pending', field: 'message' }] },
26
29
  })
27
30
  }
@@ -1,4 +1,5 @@
1
1
  import { dequal } from 'dequal/lite'
2
+ import { connectorActionContracts } from '../../connector/common/actionContract.ts'
2
3
  import { decisionTaskIssues } from '../../decision/common/decision.ts'
3
4
  import { httpRequestIssues, httpAuthHandles, httpRequestTask, httpRequestUrl } from '../../http/common/httpRequest.ts'
4
5
  import { openApiIssues, authHandles } from '../../openapi/common/openapi.ts'
@@ -22,7 +23,7 @@ import { canonicalJsonBytes, canonicalModule, canonicalRevisionGraph, digestByte
22
23
  import { nodeInputPorts, validateFlowGraph } from './graph.ts'
23
24
  import { inputValue } from './inputValue.ts'
24
25
  import { compareDiagnostics, validateModuleGraph } from './modules.ts'
25
- import { hasRetiredRef } from './schema.ts'
26
+ import { hasRetiredRef, matchesSchema } from './schema.ts'
26
27
  export { availableOutputs, graphOrder, nodeInputPorts } from './graph.ts'
27
28
  export { validateModules } from './modules.ts'
28
29
  export { matchesSchema, schemaValidationErrors, variableInputCompatible } from './schema.ts'
@@ -349,16 +350,18 @@ export async function validateFlow(revision: RevisionContent, engine: EngineCont
349
350
  for (const message of agentConfigIssues(task)) {
350
351
  checked.diagnostics.push({ code: 'agent.config-invalid', column: 0, line: 1, message, path: `/document/graph/nodes/${nodeId}/task/executor` })
351
352
  }
352
- if (task.executor.kind == 'connector' && task.executor.action == 'leina.send_notification') {
353
- const mapping = node.inputs.leinaId
354
- const value = inputValue(mapping, nodeInputPorts(node).leinaId?.value)
355
- if ((mapping?.kind != 'sources' || mapping.sources.length == 0) && (typeof value != 'string' || value.trim().length == 0)) {
353
+ if (task.executor.kind == 'connector') {
354
+ for (const constraint of connectorActionContracts[task.executor.action]?.inputConstraints ?? []) {
355
+ const mapping = node.inputs[constraint.handle]
356
+ if (mapping?.kind == 'sources' && mapping.sources.length > 0) continue
357
+ const value = inputValue(mapping, nodeInputPorts(node)[constraint.handle]?.value)
358
+ if (value !== undefined && matchesSchema(value, constraint.jsonSchema)) continue
356
359
  checked.diagnostics.push({
357
- code: 'leina.robot-required',
360
+ code: constraint.code,
358
361
  column: 0,
359
362
  line: 1,
360
- message: 'Select a Leina robot or bind its identifier to an input source.',
361
- path: `/document/graph/nodes/${nodeId}/inputs/leinaId`,
363
+ message: constraint.message,
364
+ path: `/document/graph/nodes/${nodeId}/inputs/${constraint.handle}`,
362
365
  })
363
366
  }
364
367
  }