@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.
Files changed (32) hide show
  1. package/dist/browser/{FlowCanvas-BQGBcfoL.js → FlowCanvas-CrsiDHK8.js} +50 -51
  2. package/dist/browser/{createResourceDialog-DkovGogv.js → createResourceDialog-CS3u9ga9.js} +2 -2
  3. package/dist/browser/{dialog-CFFuRM8s.js → dialog-CFlvkliO.js} +7 -2
  4. package/dist/browser/{exportSvg-CYwyYiuH.js → exportPng-BOPfjeEi.js} +236 -38
  5. package/dist/browser/{flowChanges-CN2R-m1M.js → flowChanges-pFjPHU5k.js} +326 -203
  6. package/dist/browser/{flowWorkspace-CXVL1FNv.js → flowWorkspace-CRlqc403.js} +698 -480
  7. package/dist/browser/preview.css +153 -132
  8. package/dist/browser/preview.js +921 -643
  9. package/dist/browser/ui-logo.d.ts +1 -0
  10. package/dist/browser/ui.css +29 -4
  11. package/dist/browser/ui.js +4 -1
  12. package/dist/browser/workbench.css +153 -132
  13. package/dist/browser/workbench.js +9 -9
  14. package/dist/browser/{workbenchSelect-CkOS95gX.js → workbenchSelect-Ci0KCYv9.js} +1 -1
  15. package/dist/command/index.js +41 -41
  16. package/dist/common/control-api-conformance.js +49 -19
  17. package/dist/common/control-api.d.ts +26 -6
  18. package/dist/common/control-api.js +49 -19
  19. package/dist/common/control-requests.js +6 -2
  20. package/dist/common/flow-authoring.js +6 -2
  21. package/dist/common/flow-change.js +6 -2
  22. package/dist/common/flow-encoding.js +6 -2
  23. package/dist/common/flow-semantics.js +6 -2
  24. package/dist/common/flow-state.d.ts +47 -0
  25. package/dist/common/flow-state.js +7 -1
  26. package/dist/common/mcp.js +49 -18
  27. package/dist/common/scheduler.js +6 -2
  28. package/package.json +1 -1
  29. package/src/control/common/api.ts +24 -8
  30. package/src/control/common/connectorDecoders.ts +11 -30
  31. package/src/control/common/revisionDecoders.ts +14 -0
  32. 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 };
@@ -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
- exact(source, [
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
- exact(source, [
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 != null && typeof policyRevision != "string") return invalidResponse("policyRevision: expected a string.");
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
- exact(source, [
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
- exact(entry, [
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
- exact(source, [
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
- exact(source, [
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
- exact(source, ["results", "version"]);
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
- exact(item, ["providerId", "error"]);
11004
+ requiredFields(item, ["providerId", "error"]);
11006
11005
  const error = record(item.error);
11007
- exact(error, ["code", "message"]);
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"]);
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@oomol-lab/open-flow",
3
- "version": "0.1.0-beta.79",
3
+ "version": "0.1.0-beta.80",
4
4
  "description": "TypeScript contracts and Browser runtime for Open Flow.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {
@@ -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 { exact, requiredFields, integer, invalidResponse, jsonValue, record, string } from './decoding.ts'
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
- exact(source, ['actionIds', 'allActions', 'triggerIds', 'allTriggers', 'configured', 'proxy'])
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
- exact(source, [
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 != null && typeof policyRevision != 'string') return invalidResponse('policyRevision: expected a string.')
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
- exact(source, [
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
- exact(entry, [
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
- exact(source, ['mode', 'sharedAccessDigest', 'sharedBindings', 'selectedBindings', 'version'])
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
- exact(source, ['candidates', 'mode', 'providerId', 'version'])
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
- exact(source, ['results', 'version'])
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
- exact(item, ['providerId', 'error'])
218
+ requiredFields(item, ['providerId', 'error'])
238
219
  const error = record(item.error)
239
- exact(error, ['code', 'message'])
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