@oomol-lab/open-flow 0.1.0-beta.79 → 0.1.0-beta.80
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.
- package/dist/browser/{FlowCanvas-BQGBcfoL.js → FlowCanvas-CrsiDHK8.js} +50 -51
- package/dist/browser/{createResourceDialog-DkovGogv.js → createResourceDialog-CS3u9ga9.js} +2 -2
- package/dist/browser/{dialog-CFFuRM8s.js → dialog-CFlvkliO.js} +7 -2
- package/dist/browser/{exportSvg-CYwyYiuH.js → exportPng-BOPfjeEi.js} +236 -38
- package/dist/browser/{flowChanges-CN2R-m1M.js → flowChanges-pFjPHU5k.js} +326 -203
- package/dist/browser/{flowWorkspace-CXVL1FNv.js → flowWorkspace-CRlqc403.js} +698 -480
- package/dist/browser/preview.css +153 -132
- package/dist/browser/preview.js +921 -643
- package/dist/browser/ui-logo.d.ts +1 -0
- package/dist/browser/ui.css +29 -4
- package/dist/browser/ui.js +4 -1
- package/dist/browser/workbench.css +153 -132
- package/dist/browser/workbench.js +9 -9
- package/dist/browser/{workbenchSelect-CkOS95gX.js → workbenchSelect-Ci0KCYv9.js} +1 -1
- package/dist/command/index.js +41 -41
- package/dist/common/control-api-conformance.js +49 -19
- package/dist/common/control-api.d.ts +26 -6
- package/dist/common/control-api.js +49 -19
- package/dist/common/control-requests.js +6 -2
- package/dist/common/flow-authoring.js +6 -2
- package/dist/common/flow-change.js +6 -2
- package/dist/common/flow-encoding.js +6 -2
- package/dist/common/flow-semantics.js +6 -2
- package/dist/common/flow-state.d.ts +47 -0
- package/dist/common/flow-state.js +7 -1
- package/dist/common/mcp.js +49 -18
- package/dist/common/scheduler.js +6 -2
- package/package.json +1 -1
- package/src/control/common/api.ts +24 -8
- package/src/control/common/connectorDecoders.ts +11 -30
- package/src/control/common/revisionDecoders.ts +14 -0
- package/src/state/common/state.ts +14 -0
|
@@ -5733,6 +5733,7 @@ var stateQuerySchema = strictObject({
|
|
|
5733
5733
|
scope: _enum(["draft", "live"]),
|
|
5734
5734
|
values: literal(false).optional()
|
|
5735
5735
|
});
|
|
5736
|
+
var stateSummaryQuerySchema = stateQuerySchema.omit({ values: true });
|
|
5736
5737
|
var stateReadSchema = strictObject({
|
|
5737
5738
|
scope: _enum(["draft", "live"]),
|
|
5738
5739
|
keys: array(keySchema).min(1).max(stateReadLimits.maxKeys).refine((keys) => new Set(keys).size === keys.length, "State keys must be distinct.")
|
|
@@ -5745,6 +5746,7 @@ var dedupeClearSchema = strictObject({
|
|
|
5745
5746
|
scope: _enum(["draft", "live"]),
|
|
5746
5747
|
space: string().regex(/^(flow|node:.+)$/)
|
|
5747
5748
|
});
|
|
5749
|
+
var dedupeClearAllSchema = strictObject({ scope: _enum(["draft", "live"]) });
|
|
5748
5750
|
var skipDuplicatesConfigSchema = strictObject({ fields: array(string().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) });
|
|
5749
5751
|
var skipDuplicatesExecutorSchema = skipDuplicatesConfigSchema.extend({ kind: literal("skip-duplicates") });
|
|
5750
5752
|
function skipDuplicatesTask(config = skipDuplicatesConfigSchema.parse({}), name = "Skip Duplicates") {
|
|
@@ -5822,6 +5824,10 @@ var flowStateViewSchema = object({
|
|
|
5822
5824
|
updatedAt: number()
|
|
5823
5825
|
}))
|
|
5824
5826
|
});
|
|
5827
|
+
var flowStateSummarySchema = flowStateViewSchema.extend({ values: strictObject({
|
|
5828
|
+
count: number().int().nonnegative(),
|
|
5829
|
+
updatedAt: number().nullable()
|
|
5830
|
+
}) });
|
|
5825
5831
|
var flowStateValuesSchema = object({
|
|
5826
5832
|
version: literal(1),
|
|
5827
5833
|
scope: _enum(["draft", "live"]),
|
|
@@ -5834,4 +5840,4 @@ var flowStateValuesSchema = object({
|
|
|
5834
5840
|
return response.values.reduce((bytes, item) => bytes + (item.value === null ? 0 : encoder.encode(JSON.stringify(item.value)).byteLength), 0) <= stateReadLimits.maxBytes;
|
|
5835
5841
|
}, "State values exceed the 1 MiB read limit.");
|
|
5836
5842
|
//#endregion
|
|
5837
|
-
export { StateError, dedupeClearSchema, dedupeIdentities, flowStateValuesSchema, flowStateViewSchema, hashIdentities, skipDuplicatesConfigSchema, skipDuplicatesExecutorSchema, skipDuplicatesIdentities, skipDuplicatesTask, stateClearSchema, stateConformanceCases, stateKey, stateQuerySchema, stateReadLimits, stateReadSchema };
|
|
5843
|
+
export { StateError, dedupeClearAllSchema, dedupeClearSchema, dedupeIdentities, flowStateSummarySchema, flowStateValuesSchema, flowStateViewSchema, hashIdentities, skipDuplicatesConfigSchema, skipDuplicatesExecutorSchema, skipDuplicatesIdentities, skipDuplicatesTask, stateClearSchema, stateConformanceCases, stateKey, stateQuerySchema, stateReadLimits, stateReadSchema, stateSummaryQuerySchema };
|
package/dist/common/mcp.js
CHANGED
|
@@ -8857,6 +8857,7 @@ var stateQuerySchema = strictObject({
|
|
|
8857
8857
|
scope: _enum(["draft", "live"]),
|
|
8858
8858
|
values: literal(false).optional()
|
|
8859
8859
|
});
|
|
8860
|
+
stateQuerySchema.omit({ values: true });
|
|
8860
8861
|
var stateReadSchema = strictObject({
|
|
8861
8862
|
scope: _enum(["draft", "live"]),
|
|
8862
8863
|
keys: array(keySchema).min(1).max(stateReadLimits.maxKeys).refine((keys) => new Set(keys).size === keys.length, "State keys must be distinct.")
|
|
@@ -8869,6 +8870,7 @@ var dedupeClearSchema = strictObject({
|
|
|
8869
8870
|
scope: _enum(["draft", "live"]),
|
|
8870
8871
|
space: string$1().regex(/^(flow|node:.+)$/)
|
|
8871
8872
|
});
|
|
8873
|
+
strictObject({ scope: _enum(["draft", "live"]) });
|
|
8872
8874
|
var skipDuplicatesConfigSchema = strictObject({ fields: array(string$1().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) });
|
|
8873
8875
|
var skipDuplicatesExecutorSchema = skipDuplicatesConfigSchema.extend({ kind: literal("skip-duplicates") });
|
|
8874
8876
|
var flowStateViewSchema = object({
|
|
@@ -8894,6 +8896,10 @@ var flowStateViewSchema = object({
|
|
|
8894
8896
|
updatedAt: number()
|
|
8895
8897
|
}))
|
|
8896
8898
|
});
|
|
8899
|
+
var flowStateSummarySchema = flowStateViewSchema.extend({ values: strictObject({
|
|
8900
|
+
count: number().int().nonnegative(),
|
|
8901
|
+
updatedAt: number().nullable()
|
|
8902
|
+
}) });
|
|
8897
8903
|
var flowStateValuesSchema = object({
|
|
8898
8904
|
version: literal(1),
|
|
8899
8905
|
scope: _enum(["draft", "live"]),
|
|
@@ -10795,7 +10801,7 @@ function leinaBots(value) {
|
|
|
10795
10801
|
}
|
|
10796
10802
|
function accessPermissions(value) {
|
|
10797
10803
|
const source = record(value);
|
|
10798
|
-
|
|
10804
|
+
requiredFields(source, [
|
|
10799
10805
|
"actionIds",
|
|
10800
10806
|
"allActions",
|
|
10801
10807
|
"triggerIds",
|
|
@@ -10833,19 +10839,15 @@ function accessBinding(value, candidate) {
|
|
|
10833
10839
|
const legacy = Object.hasOwn(source, "displayName") && !Object.hasOwn(source, "connectionDisplayName");
|
|
10834
10840
|
const permissions = candidate && Object.hasOwn(source, "permissions") ? accessPermissions(source.permissions) : void 0;
|
|
10835
10841
|
const permissionGroupName = source.permissionGroupName;
|
|
10836
|
-
|
|
10842
|
+
requiredFields(source, [
|
|
10837
10843
|
"accessBindingId",
|
|
10838
10844
|
"connectionId",
|
|
10839
10845
|
"source",
|
|
10840
10846
|
legacy ? "displayName" : "connectionDisplayName",
|
|
10841
|
-
...candidate && Object.hasOwn(source, "isDefault") ? ["isDefault"] : [],
|
|
10842
|
-
...candidate && permissions != null ? ["permissions"] : [],
|
|
10843
|
-
...Object.hasOwn(source, "permissionGroupName") ? ["permissionGroupName"] : [],
|
|
10844
|
-
...policyRevision == null ? [] : ["policyRevision"],
|
|
10845
10847
|
"providerId",
|
|
10846
10848
|
...candidate ? [] : ["status"]
|
|
10847
10849
|
]);
|
|
10848
|
-
if (policyRevision
|
|
10850
|
+
if (policyRevision !== void 0 && typeof policyRevision != "string") return invalidResponse("policyRevision: expected a string.");
|
|
10849
10851
|
if (candidate && isDefault !== void 0 && typeof isDefault != "boolean") return invalidResponse("isDefault: expected a boolean.");
|
|
10850
10852
|
if (permissionGroupName !== void 0 && permissionGroupName !== null && typeof permissionGroupName != "string") return invalidResponse("permissionGroupName: expected a string or null.");
|
|
10851
10853
|
if (!candidate && status != "active" && status != "forbidden" && status != "invalid" && status != "missing") return invalidResponse();
|
|
@@ -10874,13 +10876,11 @@ function accessMode(value) {
|
|
|
10874
10876
|
}
|
|
10875
10877
|
function connectorAccess(value) {
|
|
10876
10878
|
const source = record(value);
|
|
10877
|
-
|
|
10879
|
+
requiredFields(source, [
|
|
10878
10880
|
"accessRevision",
|
|
10879
10881
|
"bindings",
|
|
10880
10882
|
"mode",
|
|
10881
10883
|
"sharedAccessDigest",
|
|
10882
|
-
..."providerIds" in source ? ["providerIds"] : [],
|
|
10883
|
-
..."discardedBindingCount" in source ? ["discardedBindingCount"] : [],
|
|
10884
10884
|
"version"
|
|
10885
10885
|
]);
|
|
10886
10886
|
const accessRevision = integer(source.accessRevision);
|
|
@@ -10921,13 +10921,12 @@ function accessGrants(value) {
|
|
|
10921
10921
|
if (!Array.isArray(value)) return invalidResponse();
|
|
10922
10922
|
const bindings = value.map((candidate) => {
|
|
10923
10923
|
const entry = record(candidate);
|
|
10924
|
-
|
|
10924
|
+
requiredFields(entry, [
|
|
10925
10925
|
"accessBindingId",
|
|
10926
10926
|
"connectionId",
|
|
10927
10927
|
"providerId",
|
|
10928
10928
|
"source",
|
|
10929
|
-
"connectionDisplayName"
|
|
10930
|
-
..."permissionGroupName" in entry ? ["permissionGroupName"] : []
|
|
10929
|
+
"connectionDisplayName"
|
|
10931
10930
|
]);
|
|
10932
10931
|
let identity;
|
|
10933
10932
|
try {
|
|
@@ -10950,7 +10949,7 @@ function accessGrants(value) {
|
|
|
10950
10949
|
}
|
|
10951
10950
|
function connectorAccessSnapshot(value) {
|
|
10952
10951
|
const source = record(value);
|
|
10953
|
-
|
|
10952
|
+
requiredFields(source, [
|
|
10954
10953
|
"mode",
|
|
10955
10954
|
"sharedAccessDigest",
|
|
10956
10955
|
"sharedBindings",
|
|
@@ -10972,7 +10971,7 @@ function connectorAccessSnapshot(value) {
|
|
|
10972
10971
|
}
|
|
10973
10972
|
function connectorAccessCandidates(value, providerId) {
|
|
10974
10973
|
const source = record(value);
|
|
10975
|
-
|
|
10974
|
+
requiredFields(source, [
|
|
10976
10975
|
"candidates",
|
|
10977
10976
|
"mode",
|
|
10978
10977
|
"providerId",
|
|
@@ -10993,7 +10992,7 @@ function connectorAccessCandidates(value, providerId) {
|
|
|
10993
10992
|
}
|
|
10994
10993
|
function connectorAccessCandidatesBatch(value, providerIds) {
|
|
10995
10994
|
const source = record(value);
|
|
10996
|
-
|
|
10995
|
+
requiredFields(source, ["results", "version"]);
|
|
10997
10996
|
if (source.version != 1) return invalidResponse("version: expected 1.");
|
|
10998
10997
|
if (!Array.isArray(source.results)) return invalidResponse("results: expected an array.");
|
|
10999
10998
|
const remaining = new Set(providerIds);
|
|
@@ -11002,9 +11001,9 @@ function connectorAccessCandidatesBatch(value, providerIds) {
|
|
|
11002
11001
|
const providerId = string(item.providerId);
|
|
11003
11002
|
if (!remaining.delete(providerId)) return invalidResponse("results[].providerId: duplicate or unrequested provider.");
|
|
11004
11003
|
if (!Object.hasOwn(item, "error")) return connectorAccessCandidates(item, providerId);
|
|
11005
|
-
|
|
11004
|
+
requiredFields(item, ["providerId", "error"]);
|
|
11006
11005
|
const error = record(item.error);
|
|
11007
|
-
|
|
11006
|
+
requiredFields(error, ["code", "message"]);
|
|
11008
11007
|
return {
|
|
11009
11008
|
providerId,
|
|
11010
11009
|
error: {
|
|
@@ -11640,6 +11639,25 @@ function draft(value) {
|
|
|
11640
11639
|
return invalidResponse();
|
|
11641
11640
|
}
|
|
11642
11641
|
}
|
|
11642
|
+
function revisionTriggers(value) {
|
|
11643
|
+
const source = record(value);
|
|
11644
|
+
const graph = record(record(record(source.content).document).graph);
|
|
11645
|
+
const triggers = Object.entries(record(graph.nodes)).flatMap(([triggerId, entry]) => {
|
|
11646
|
+
const node = record(entry);
|
|
11647
|
+
if (node.kind == "outputs" || Object.hasOwn(node, "inputs")) return [];
|
|
11648
|
+
const kind = string(node.kind);
|
|
11649
|
+
if (node.name !== void 0 && typeof node.name != "string") return invalidResponse();
|
|
11650
|
+
return [{
|
|
11651
|
+
triggerId: string(triggerId),
|
|
11652
|
+
kind,
|
|
11653
|
+
...node.name === void 0 ? {} : { name: node.name }
|
|
11654
|
+
}];
|
|
11655
|
+
});
|
|
11656
|
+
return {
|
|
11657
|
+
...revisionMetadata(source),
|
|
11658
|
+
triggers
|
|
11659
|
+
};
|
|
11660
|
+
}
|
|
11643
11661
|
function presentation(value) {
|
|
11644
11662
|
const source = record(value);
|
|
11645
11663
|
if (source.version != 1 || integer(source.revision) < 1) return invalidResponse();
|
|
@@ -12385,6 +12403,10 @@ var ControlClient = class {
|
|
|
12385
12403
|
const result = flowStateViewSchema.safeParse(await this.request(`/v1/flows/${segment(flowId)}/state?scope=${scope}`));
|
|
12386
12404
|
return result.success && result.data.scope === scope ? result.data : invalidResponse();
|
|
12387
12405
|
}
|
|
12406
|
+
async getFlowStateSummary(flowId, scope, signal) {
|
|
12407
|
+
const result = flowStateSummarySchema.safeParse(await this.request(`/v1/flows/${segment(flowId)}/state/summary?scope=${scope}`, { signal }));
|
|
12408
|
+
return result.success && result.data.scope === scope ? result.data : invalidResponse();
|
|
12409
|
+
}
|
|
12388
12410
|
async getFlowStateValues(flowId, scope, keys, signal) {
|
|
12389
12411
|
const result = flowStateValuesSchema.safeParse(await this.request(`/v1/flows/${segment(flowId)}/state/read`, {
|
|
12390
12412
|
method: "POST",
|
|
@@ -12405,6 +12427,10 @@ var ControlClient = class {
|
|
|
12405
12427
|
const source = record(await this.request(`/v1/flows/${segment(flowId)}/dedupe?scope=${scope}&space=${encodeURIComponent(space)}`, { method: "DELETE" }));
|
|
12406
12428
|
if (source.version !== 1 || source.cleared !== true) invalidResponse();
|
|
12407
12429
|
}
|
|
12430
|
+
async clearAllFlowDedupe(flowId, scope) {
|
|
12431
|
+
const source = record(await this.request(`/v1/flows/${segment(flowId)}/dedupe/all?scope=${scope}`, { method: "DELETE" }));
|
|
12432
|
+
if (source.version !== 1 || source.cleared !== true) invalidResponse();
|
|
12433
|
+
}
|
|
12408
12434
|
async renameFlow(flowId, name) {
|
|
12409
12435
|
return this.updateFlow(flowId, { name });
|
|
12410
12436
|
}
|
|
@@ -12517,6 +12543,11 @@ var ControlClient = class {
|
|
|
12517
12543
|
async getRevision(flowId, revisionId, signal) {
|
|
12518
12544
|
return draft(await this.request(`/v1/flows/${segment(flowId)}/revisions/${segment(revisionId)}`, { signal }));
|
|
12519
12545
|
}
|
|
12546
|
+
async getRevisionTriggers(flowId, revisionId, signal) {
|
|
12547
|
+
const revision = revisionTriggers(await this.request(`/v1/flows/${segment(flowId)}/revisions/${segment(revisionId)}`, { signal }));
|
|
12548
|
+
if (revision.flowId != flowId || revision.revisionId != revisionId) return invalidResponse();
|
|
12549
|
+
return revision;
|
|
12550
|
+
}
|
|
12520
12551
|
async listTriggerConfigOptions(flowId, nodeId, field, signal) {
|
|
12521
12552
|
const source = record(await this.request(`/v1/flows/${segment(flowId)}/triggers/${segment(nodeId)}/options/${segment(field)}`, { signal }));
|
|
12522
12553
|
requiredFields(source, ["options", "version"]);
|
package/dist/common/scheduler.js
CHANGED
|
@@ -9545,7 +9545,7 @@ var keySchema = string$1().min(1).refine((value) => new TextEncoder().encode(val
|
|
|
9545
9545
|
strictObject({
|
|
9546
9546
|
scope: _enum(["draft", "live"]),
|
|
9547
9547
|
values: literal(false).optional()
|
|
9548
|
-
});
|
|
9548
|
+
}).omit({ values: true });
|
|
9549
9549
|
strictObject({
|
|
9550
9550
|
scope: _enum(["draft", "live"]),
|
|
9551
9551
|
keys: array(keySchema).min(1).max(stateReadLimits.maxKeys).refine((keys) => new Set(keys).size === keys.length, "State keys must be distinct.")
|
|
@@ -9558,6 +9558,7 @@ strictObject({
|
|
|
9558
9558
|
scope: _enum(["draft", "live"]),
|
|
9559
9559
|
space: string$1().regex(/^(flow|node:.+)$/)
|
|
9560
9560
|
});
|
|
9561
|
+
strictObject({ scope: _enum(["draft", "live"]) });
|
|
9561
9562
|
var skipDuplicatesExecutorSchema = strictObject({ fields: array(string$1().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) }).extend({ kind: literal("skip-duplicates") });
|
|
9562
9563
|
object({
|
|
9563
9564
|
version: literal(1),
|
|
@@ -9581,7 +9582,10 @@ object({
|
|
|
9581
9582
|
reservedCount: number().nonnegative(),
|
|
9582
9583
|
updatedAt: number()
|
|
9583
9584
|
}))
|
|
9584
|
-
})
|
|
9585
|
+
}).extend({ values: strictObject({
|
|
9586
|
+
count: number().int().nonnegative(),
|
|
9587
|
+
updatedAt: number().nullable()
|
|
9588
|
+
}) });
|
|
9585
9589
|
object({
|
|
9586
9590
|
version: literal(1),
|
|
9587
9591
|
scope: _enum(["draft", "live"]),
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CanvasCommand } from '../../canvas/common/commands.ts'
|
|
2
|
-
import type { FlowStateValues, FlowStateView, StateScope } from '../../state/common/state.ts'
|
|
2
|
+
import type { FlowStateSummary, FlowStateValues, FlowStateView, StateScope } from '../../state/common/state.ts'
|
|
3
3
|
|
|
4
4
|
import { currentEngineContract } from '../../runtime/common/engineContract.ts'
|
|
5
|
-
import { flowStateValuesSchema, flowStateViewSchema } from '../../state/common/state.ts'
|
|
5
|
+
import { flowStateSummarySchema, flowStateValuesSchema, flowStateViewSchema } from '../../state/common/state.ts'
|
|
6
6
|
import { workflowContractSchema, workflowReferencesSchema } from '../../workflow/common/workflow.ts'
|
|
7
7
|
export type { CanvasCommand, CanvasCommandSubscriber } from '../../canvas/common/commands.ts'
|
|
8
8
|
import type { LiveDetails } from './liveDetails.ts'
|
|
@@ -68,7 +68,7 @@ import { allConnectorConnectionsQuery, connectorActionQuery, connectorConnection
|
|
|
68
68
|
import { requiredFields, integer, invalidResponse, jsonValue, record, string } from './decoding.ts'
|
|
69
69
|
import { errorListener, flow, flowPage, variable, variableListItem, variableReferences } from './flowDecoders.ts'
|
|
70
70
|
import { live, publication, publicationPage, publishOperation } from './publicationDecoders.ts'
|
|
71
|
-
import { draft, draftChange, draftSync, presentation } from './revisionDecoders.ts'
|
|
71
|
+
import { draft, draftChange, draftSync, presentation, revisionTriggers } from './revisionDecoders.ts'
|
|
72
72
|
import { runCancellation, runDetails, runPage, runResult, waitResolution } from './runDecoders.ts'
|
|
73
73
|
import { pollTriggerTestResult, triggerActivityPage, triggerBinding, triggerBindingDetail, triggerKey, triggerKeySummary } from './triggerDecoders.ts'
|
|
74
74
|
|
|
@@ -968,7 +968,7 @@ export class ControlClient {
|
|
|
968
968
|
)
|
|
969
969
|
}
|
|
970
970
|
|
|
971
|
-
async broadcastCanvas(flowId: string, command: CanvasCommand): Promise<CanvasBroadcast> {
|
|
971
|
+
async broadcastCanvas(flowId: string, command: CanvasCommand | Readonly<Record<string, unknown>>): Promise<CanvasBroadcast> {
|
|
972
972
|
const source = record(
|
|
973
973
|
await this.request(`/v1/flows/${segment(flowId)}/canvas`, {
|
|
974
974
|
method: 'POST',
|
|
@@ -1022,6 +1022,11 @@ export class ControlClient {
|
|
|
1022
1022
|
return result.success && result.data.scope === scope ? result.data : invalidResponse()
|
|
1023
1023
|
}
|
|
1024
1024
|
|
|
1025
|
+
async getFlowStateSummary(flowId: string, scope: StateScope, signal?: AbortSignal): Promise<FlowStateSummary> {
|
|
1026
|
+
const result = flowStateSummarySchema.safeParse(await this.request(`/v1/flows/${segment(flowId)}/state/summary?scope=${scope}`, { signal }))
|
|
1027
|
+
return result.success && result.data.scope === scope ? result.data : invalidResponse()
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1025
1030
|
async getFlowStateValues(flowId: string, scope: StateScope, keys: readonly string[], signal?: AbortSignal): Promise<FlowStateValues> {
|
|
1026
1031
|
const result = flowStateValuesSchema.safeParse(
|
|
1027
1032
|
await this.request(`/v1/flows/${segment(flowId)}/state/read`, { method: 'POST', body: JSON.stringify({ version: 1, scope, keys }), signal }),
|
|
@@ -1046,6 +1051,11 @@ export class ControlClient {
|
|
|
1046
1051
|
if (source.version !== 1 || source.cleared !== true) invalidResponse()
|
|
1047
1052
|
}
|
|
1048
1053
|
|
|
1054
|
+
async clearAllFlowDedupe(flowId: string, scope: StateScope): Promise<void> {
|
|
1055
|
+
const source = record(await this.request(`/v1/flows/${segment(flowId)}/dedupe/all?scope=${scope}`, { method: 'DELETE' }))
|
|
1056
|
+
if (source.version !== 1 || source.cleared !== true) invalidResponse()
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1049
1059
|
async renameFlow(flowId: string, name: string): Promise<Flow> {
|
|
1050
1060
|
return this.updateFlow(flowId, { name })
|
|
1051
1061
|
}
|
|
@@ -1168,6 +1178,12 @@ export class ControlClient {
|
|
|
1168
1178
|
return draft(await this.request(`/v1/flows/${segment(flowId)}/revisions/${segment(revisionId)}`, { signal }))
|
|
1169
1179
|
}
|
|
1170
1180
|
|
|
1181
|
+
async getRevisionTriggers(flowId: string, revisionId: string, signal?: AbortSignal) {
|
|
1182
|
+
const revision = revisionTriggers(await this.request(`/v1/flows/${segment(flowId)}/revisions/${segment(revisionId)}`, { signal }))
|
|
1183
|
+
if (revision.flowId != flowId || revision.revisionId != revisionId) return invalidResponse()
|
|
1184
|
+
return revision
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1171
1187
|
async listTriggerConfigOptions(flowId: string, nodeId: string, field: string, signal?: AbortSignal): Promise<readonly TriggerConfigOption[]> {
|
|
1172
1188
|
const source = record(await this.request(`/v1/flows/${segment(flowId)}/triggers/${segment(nodeId)}/options/${segment(field)}`, { signal }))
|
|
1173
1189
|
requiredFields(source, ['options', 'version'])
|
|
@@ -1315,19 +1331,19 @@ export class ControlClient {
|
|
|
1315
1331
|
}
|
|
1316
1332
|
}
|
|
1317
1333
|
|
|
1318
|
-
async readAuthoring(flowId: string, query: AuthoringRead = {}) {
|
|
1334
|
+
async readAuthoring(flowId: string, query: AuthoringRead | Readonly<Record<string, unknown>> = {}) {
|
|
1319
1335
|
return authoringReadResponse.parse(await this.request(`/v1/flows/${segment(flowId)}/authoring/read`, { method: 'POST', body: JSON.stringify(query) }))
|
|
1320
1336
|
}
|
|
1321
1337
|
|
|
1322
|
-
async searchAuthoring(flowId: string, query: AuthoringSearch) {
|
|
1338
|
+
async searchAuthoring(flowId: string, query: AuthoringSearch | Readonly<Record<string, unknown>>) {
|
|
1323
1339
|
return authoringSearchResponse.parse(await this.request(`/v1/flows/${segment(flowId)}/authoring/search`, { method: 'POST', body: JSON.stringify(query) }))
|
|
1324
1340
|
}
|
|
1325
1341
|
|
|
1326
|
-
async authoringSchema(flowId: string, query: { type?: AuthoringType; action?: string }) {
|
|
1342
|
+
async authoringSchema(flowId: string, query: { type?: AuthoringType; action?: string } | Readonly<Record<string, unknown>>) {
|
|
1327
1343
|
return record(await this.request(`/v1/flows/${segment(flowId)}/authoring/schema`, { method: 'POST', body: JSON.stringify(query) }))
|
|
1328
1344
|
}
|
|
1329
1345
|
|
|
1330
|
-
async editAuthoring(flowId: string, request: AuthoringRequest) {
|
|
1346
|
+
async editAuthoring(flowId: string, request: AuthoringRequest | { readonly requestId: string; readonly [key: string]: unknown }) {
|
|
1331
1347
|
return authoringEditResponse.parse(
|
|
1332
1348
|
await this.request(`/v1/flows/${segment(flowId)}/authoring/edit`, {
|
|
1333
1349
|
method: 'POST',
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
} from './api.ts'
|
|
16
16
|
|
|
17
17
|
import { providerIconAppearance } from '../../connector/common/providerIconSprite.ts'
|
|
18
|
-
import {
|
|
18
|
+
import { requiredFields, integer, invalidResponse, jsonValue, record, string } from './decoding.ts'
|
|
19
19
|
import { ApiError } from './errors.ts'
|
|
20
20
|
import { parseProviderAccessSource } from './providerAccess.ts'
|
|
21
21
|
|
|
@@ -33,7 +33,7 @@ export function leinaBots(value: unknown): readonly LeinaBot[] {
|
|
|
33
33
|
|
|
34
34
|
function accessPermissions(value: unknown): NonNullable<ProviderAccessBindingCandidate['permissions']> {
|
|
35
35
|
const source = record(value)
|
|
36
|
-
|
|
36
|
+
requiredFields(source, ['actionIds', 'allActions', 'triggerIds', 'allTriggers', 'configured', 'proxy'])
|
|
37
37
|
if (!Array.isArray(source.actionIds)) return invalidResponse('permissions.actionIds: expected an array.')
|
|
38
38
|
if (!Array.isArray(source.triggerIds)) return invalidResponse('permissions.triggerIds: expected an array.')
|
|
39
39
|
if (typeof source.allActions != 'boolean') return invalidResponse('permissions.allActions: expected a boolean.')
|
|
@@ -62,19 +62,15 @@ function accessBinding(value: unknown, candidate: boolean): ProviderAccessBindin
|
|
|
62
62
|
const legacy = Object.hasOwn(source, 'displayName') && !Object.hasOwn(source, 'connectionDisplayName')
|
|
63
63
|
const permissions = candidate && Object.hasOwn(source, 'permissions') ? accessPermissions(source.permissions) : undefined
|
|
64
64
|
const permissionGroupName = source.permissionGroupName
|
|
65
|
-
|
|
65
|
+
requiredFields(source, [
|
|
66
66
|
'accessBindingId',
|
|
67
67
|
'connectionId',
|
|
68
68
|
'source',
|
|
69
69
|
legacy ? 'displayName' : 'connectionDisplayName',
|
|
70
|
-
...(candidate && Object.hasOwn(source, 'isDefault') ? ['isDefault'] : []),
|
|
71
|
-
...(candidate && permissions != null ? ['permissions'] : []),
|
|
72
|
-
...(Object.hasOwn(source, 'permissionGroupName') ? ['permissionGroupName'] : []),
|
|
73
|
-
...(policyRevision == null ? [] : ['policyRevision']),
|
|
74
70
|
'providerId',
|
|
75
71
|
...(candidate ? [] : ['status']),
|
|
76
72
|
])
|
|
77
|
-
if (policyRevision
|
|
73
|
+
if (policyRevision !== undefined && typeof policyRevision != 'string') return invalidResponse('policyRevision: expected a string.')
|
|
78
74
|
if (candidate && isDefault !== undefined && typeof isDefault != 'boolean') return invalidResponse('isDefault: expected a boolean.')
|
|
79
75
|
if (permissionGroupName !== undefined && permissionGroupName !== null && typeof permissionGroupName != 'string')
|
|
80
76
|
return invalidResponse('permissionGroupName: expected a string or null.')
|
|
@@ -106,15 +102,7 @@ function accessMode(value: unknown): ConnectorAccess['mode'] {
|
|
|
106
102
|
|
|
107
103
|
export function connectorAccess(value: unknown): ConnectorAccess {
|
|
108
104
|
const source = record(value)
|
|
109
|
-
|
|
110
|
-
'accessRevision',
|
|
111
|
-
'bindings',
|
|
112
|
-
'mode',
|
|
113
|
-
'sharedAccessDigest',
|
|
114
|
-
...('providerIds' in source ? ['providerIds'] : []),
|
|
115
|
-
...('discardedBindingCount' in source ? ['discardedBindingCount'] : []),
|
|
116
|
-
'version',
|
|
117
|
-
])
|
|
105
|
+
requiredFields(source, ['accessRevision', 'bindings', 'mode', 'sharedAccessDigest', 'version'])
|
|
118
106
|
const accessRevision = integer(source.accessRevision)
|
|
119
107
|
if (source.version != 1 || accessRevision < 0 || !Array.isArray(source.bindings)) return invalidResponse()
|
|
120
108
|
if ('providerIds' in source && !Array.isArray(source.providerIds)) return invalidResponse()
|
|
@@ -170,14 +158,7 @@ function accessGrants(value: unknown): readonly ConnectorAccessGrant[] {
|
|
|
170
158
|
if (!Array.isArray(value)) return invalidResponse()
|
|
171
159
|
const bindings = value.map((candidate) => {
|
|
172
160
|
const entry = record(candidate)
|
|
173
|
-
|
|
174
|
-
'accessBindingId',
|
|
175
|
-
'connectionId',
|
|
176
|
-
'providerId',
|
|
177
|
-
'source',
|
|
178
|
-
'connectionDisplayName',
|
|
179
|
-
...('permissionGroupName' in entry ? ['permissionGroupName'] : []),
|
|
180
|
-
])
|
|
161
|
+
requiredFields(entry, ['accessBindingId', 'connectionId', 'providerId', 'source', 'connectionDisplayName'])
|
|
181
162
|
let identity
|
|
182
163
|
try {
|
|
183
164
|
identity = parseProviderAccessSource(entry.source)
|
|
@@ -200,7 +181,7 @@ function accessGrants(value: unknown): readonly ConnectorAccessGrant[] {
|
|
|
200
181
|
|
|
201
182
|
export function connectorAccessSnapshot(value: unknown): ConnectorAccessSnapshot {
|
|
202
183
|
const source = record(value)
|
|
203
|
-
|
|
184
|
+
requiredFields(source, ['mode', 'sharedAccessDigest', 'sharedBindings', 'selectedBindings', 'version'])
|
|
204
185
|
if (source.version != 2) return invalidResponse()
|
|
205
186
|
const mode = accessMode(source.mode)
|
|
206
187
|
const sharedBindings = accessGrants(source.sharedBindings)
|
|
@@ -211,7 +192,7 @@ export function connectorAccessSnapshot(value: unknown): ConnectorAccessSnapshot
|
|
|
211
192
|
|
|
212
193
|
export function connectorAccessCandidates(value: unknown, providerId: string): ConnectorAccessCandidates {
|
|
213
194
|
const source = record(value)
|
|
214
|
-
|
|
195
|
+
requiredFields(source, ['candidates', 'mode', 'providerId', 'version'])
|
|
215
196
|
if (source.version != 1) return invalidResponse('version: expected 1.')
|
|
216
197
|
if (string(source.providerId) != providerId) return invalidResponse('providerId: does not match the requested provider.')
|
|
217
198
|
if (!Array.isArray(source.candidates)) return invalidResponse('candidates: expected an array.')
|
|
@@ -225,7 +206,7 @@ export function connectorAccessCandidates(value: unknown, providerId: string): C
|
|
|
225
206
|
|
|
226
207
|
export function connectorAccessCandidatesBatch(value: unknown, providerIds: readonly string[]): ConnectorAccessCandidatesBatch {
|
|
227
208
|
const source = record(value)
|
|
228
|
-
|
|
209
|
+
requiredFields(source, ['results', 'version'])
|
|
229
210
|
if (source.version != 1) return invalidResponse('version: expected 1.')
|
|
230
211
|
if (!Array.isArray(source.results)) return invalidResponse('results: expected an array.')
|
|
231
212
|
const remaining = new Set(providerIds)
|
|
@@ -234,9 +215,9 @@ export function connectorAccessCandidatesBatch(value: unknown, providerIds: read
|
|
|
234
215
|
const providerId = string(item.providerId)
|
|
235
216
|
if (!remaining.delete(providerId)) return invalidResponse('results[].providerId: duplicate or unrequested provider.')
|
|
236
217
|
if (!Object.hasOwn(item, 'error')) return connectorAccessCandidates(item, providerId)
|
|
237
|
-
|
|
218
|
+
requiredFields(item, ['providerId', 'error'])
|
|
238
219
|
const error = record(item.error)
|
|
239
|
-
|
|
220
|
+
requiredFields(error, ['code', 'message'])
|
|
240
221
|
return { providerId, error: { code: string(error.code), message: string(error.message) } }
|
|
241
222
|
})
|
|
242
223
|
if (remaining.size != 0) return invalidResponse('results: missing requested providers.')
|
|
@@ -28,6 +28,20 @@ export function draft(value: unknown): Draft {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export function revisionTriggers(value: unknown) {
|
|
32
|
+
const source = record(value)
|
|
33
|
+
const content = record(source.content)
|
|
34
|
+
const graph = record(record(content.document).graph)
|
|
35
|
+
const triggers = Object.entries(record(graph.nodes)).flatMap(([triggerId, entry]) => {
|
|
36
|
+
const node = record(entry)
|
|
37
|
+
if (node.kind == 'outputs' || Object.hasOwn(node, 'inputs')) return []
|
|
38
|
+
const kind = string(node.kind)
|
|
39
|
+
if (node.name !== undefined && typeof node.name != 'string') return invalidResponse()
|
|
40
|
+
return [{ triggerId: string(triggerId), kind, ...(node.name === undefined ? {} : { name: node.name }) }]
|
|
41
|
+
})
|
|
42
|
+
return { ...revisionMetadata(source), triggers }
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
export function presentation(value: unknown): Presentation {
|
|
32
46
|
const source = record(value)
|
|
33
47
|
if (source.version != 1 || integer(source.revision) < 1) return invalidResponse()
|
|
@@ -11,6 +11,7 @@ const keySchema = z
|
|
|
11
11
|
.min(1)
|
|
12
12
|
.refine((value) => new TextEncoder().encode(value).length <= 256)
|
|
13
13
|
export const stateQuerySchema = z.strictObject({ scope: z.enum(['draft', 'live']), values: z.literal(false).optional() })
|
|
14
|
+
export const stateSummaryQuerySchema = stateQuerySchema.omit({ values: true })
|
|
14
15
|
export const stateReadSchema = z.strictObject({
|
|
15
16
|
scope: z.enum(['draft', 'live']),
|
|
16
17
|
keys: z
|
|
@@ -25,6 +26,7 @@ export const stateClearSchema = z.strictObject({
|
|
|
25
26
|
key: keySchema.optional(),
|
|
26
27
|
})
|
|
27
28
|
export const dedupeClearSchema = z.strictObject({ scope: z.enum(['draft', 'live']), space: z.string().regex(/^(flow|node:.+)$/) })
|
|
29
|
+
export const dedupeClearAllSchema = z.strictObject({ scope: z.enum(['draft', 'live']) })
|
|
28
30
|
export type DedupeIdentity = string | number
|
|
29
31
|
export const skipDuplicatesConfigSchema = z.strictObject({
|
|
30
32
|
fields: z
|
|
@@ -150,6 +152,18 @@ export const flowStateViewSchema = z.object({
|
|
|
150
152
|
),
|
|
151
153
|
})
|
|
152
154
|
|
|
155
|
+
export interface FlowStateSummary {
|
|
156
|
+
readonly version: 1
|
|
157
|
+
readonly scope: StateScope
|
|
158
|
+
readonly limits: FlowStateView['limits']
|
|
159
|
+
readonly values: { readonly count: number; readonly updatedAt: number | null }
|
|
160
|
+
readonly spaces: FlowStateView['spaces']
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export const flowStateSummarySchema = flowStateViewSchema.extend({
|
|
164
|
+
values: z.strictObject({ count: z.number().int().nonnegative(), updatedAt: z.number().nullable() }),
|
|
165
|
+
})
|
|
166
|
+
|
|
153
167
|
export interface FlowStateValues {
|
|
154
168
|
readonly version: 1
|
|
155
169
|
readonly scope: StateScope
|