@oomol-lab/open-flow 0.1.0-beta.57 → 0.1.0-beta.59

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.
@@ -4172,6 +4172,60 @@ html[data-overlayscrollbars-viewport~=scrollbarHidden] > body::-webkit-scrollbar
4172
4172
  background-color: #0000;
4173
4173
  }
4174
4174
 
4175
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:h-auto:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4176
+ height: auto;
4177
+ }
4178
+
4179
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:flex-none:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4180
+ flex: none;
4181
+ }
4182
+
4183
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:rounded-\[6px\]:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4184
+ border-radius: 6px;
4185
+ }
4186
+
4187
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:border-0:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4188
+ border-style: var(--tw-border-style);
4189
+ border-width: 0;
4190
+ }
4191
+
4192
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:px-3:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4193
+ padding-inline: calc(var(--spacing) * 3);
4194
+ }
4195
+
4196
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:py-2:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4197
+ padding-block: calc(var(--spacing) * 2);
4198
+ }
4199
+
4200
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:text-\[13px\]:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4201
+ font-size: 13px;
4202
+ }
4203
+
4204
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:font-normal:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4205
+ --tw-font-weight: var(--font-weight-normal);
4206
+ font-weight: var(--font-weight-normal);
4207
+ }
4208
+
4209
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:text-muted-foreground:is(:where(.group\/tabs-list)[data-variant="navigation"] *) {
4210
+ color: var(--ui-muted-foreground);
4211
+ }
4212
+
4213
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:group-data-\[size\=sm\]\/tabs-list\:rounded-\[4px\]:is(:where(.group\/tabs-list)[data-variant="navigation"] *):is(:where(.group\/tabs-list)[data-size="sm"] *) {
4214
+ border-radius: 4px;
4215
+ }
4216
+
4217
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:group-data-\[size\=sm\]\/tabs-list\:px-2:is(:where(.group\/tabs-list)[data-variant="navigation"] *):is(:where(.group\/tabs-list)[data-size="sm"] *) {
4218
+ padding-inline: calc(var(--spacing) * 2);
4219
+ }
4220
+
4221
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:group-data-\[size\=sm\]\/tabs-list\:py-1:is(:where(.group\/tabs-list)[data-variant="navigation"] *):is(:where(.group\/tabs-list)[data-size="sm"] *) {
4222
+ padding-block: var(--spacing);
4223
+ }
4224
+
4225
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:group-data-\[size\=sm\]\/tabs-list\:text-\[12px\]:is(:where(.group\/tabs-list)[data-variant="navigation"] *):is(:where(.group\/tabs-list)[data-size="sm"] *) {
4226
+ font-size: 12px;
4227
+ }
4228
+
4175
4229
  :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled ~ *) {
4176
4230
  cursor: not-allowed;
4177
4231
  }
@@ -4492,6 +4546,14 @@ html[data-overlayscrollbars-viewport~=scrollbarHidden] > body::-webkit-scrollbar
4492
4546
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
4493
4547
  }
4494
4548
 
4549
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:hover\:bg-muted:is(:where(.group\/tabs-list)[data-variant="navigation"] *):hover {
4550
+ background-color: var(--ui-muted);
4551
+ }
4552
+
4553
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:hover\:text-foreground:is(:where(.group\/tabs-list)[data-variant="navigation"] *):hover {
4554
+ color: var(--ui-foreground);
4555
+ }
4556
+
4495
4557
  :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .hover\:placeholder\:opacity-50:hover::placeholder {
4496
4558
  opacity: .5;
4497
4559
  }
@@ -5035,6 +5097,19 @@ html[data-overlayscrollbars-viewport~=scrollbarHidden] > body::-webkit-scrollbar
5035
5097
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
5036
5098
  }
5037
5099
 
5100
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:data-active\:bg-muted:is(:where(.group\/tabs-list)[data-variant="navigation"] *)[data-active] {
5101
+ background-color: var(--ui-muted);
5102
+ }
5103
+
5104
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:data-active\:font-semibold:is(:where(.group\/tabs-list)[data-variant="navigation"] *)[data-active] {
5105
+ --tw-font-weight: var(--font-weight-semibold);
5106
+ font-weight: var(--font-weight-semibold);
5107
+ }
5108
+
5109
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=navigation\]\/tabs-list\:data-active\:text-foreground:is(:where(.group\/tabs-list)[data-variant="navigation"] *)[data-active] {
5110
+ color: var(--ui-foreground);
5111
+ }
5112
+
5038
5113
  :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[variant\=line\]\/tabs-list\:data-active\:after\:opacity-100:is(:where(.group\/tabs-list)[data-variant="line"] *)[data-active]:after {
5039
5114
  content: var(--tw-content);
5040
5115
  opacity: 1;
@@ -5577,6 +5652,14 @@ html[data-overlayscrollbars-viewport~=scrollbarHidden] > body::-webkit-scrollbar
5577
5652
  border-radius: 0;
5578
5653
  }
5579
5654
 
5655
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .data-\[variant\=navigation\]\:p-0[data-variant="navigation"] {
5656
+ padding: 0;
5657
+ }
5658
+
5659
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .group-data-\[orientation\=horizontal\]\/tabs\:data-\[variant\=navigation\]\:h-auto:is(:where(.group\/tabs)[data-orientation="horizontal"] *)[data-variant="navigation"] {
5660
+ height: auto;
5661
+ }
5662
+
5580
5663
  :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .data-\[variant\=outline\]\:bg-background[data-variant="outline"] {
5581
5664
  background-color: var(--ui-background);
5582
5665
  }
@@ -6011,6 +6094,14 @@ html[data-overlayscrollbars-viewport~=scrollbarHidden] > body::-webkit-scrollbar
6011
6094
  background-color: #0000;
6012
6095
  }
6013
6096
 
6097
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .dark\:group-data-\[variant\=navigation\]\/tabs-list\:data-active\:bg-muted:where([data-theme="dark"], [data-theme="dark"] *):is(:where(.group\/tabs-list)[data-variant="navigation"] *)[data-active] {
6098
+ background-color: var(--ui-muted);
6099
+ }
6100
+
6101
+ :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .dark\:group-data-\[variant\=navigation\]\/tabs-list\:data-active\:text-foreground:where([data-theme="dark"], [data-theme="dark"] *):is(:where(.group\/tabs-list)[data-variant="navigation"] *)[data-active] {
6102
+ color: var(--ui-foreground);
6103
+ }
6104
+
6014
6105
  :where(.open-flow-theme, .open-flow-workbench, .open-flow-canvas-root) .dark\:data-checked\:bg-primary-foreground:where([data-theme="dark"], [data-theme="dark"] *)[data-checked] {
6015
6106
  background-color: var(--ui-primary-foreground);
6016
6107
  }
@@ -1,4 +1,4 @@
1
- import { $ as RunStore, $t as EventSourcesPage, An as providerIconAppearance, At as followWorkbenchLink, B as CacheResource, Bn as invalidResponse, Cn as sampleErrorOutputs, Ct as setCodeActions, Dt as updateTriggerConfig, Et as setTriggerConnection, F as feishuResourceKind, Gn as controlErrorCode, Gt as InputGroupAddon, H as resourceData, Hn as record, I as supportsFeishuChatFilter, J as providerIcon, Jt as EmptyContent, Kt as InputGroupInput, L as combineSources, Lt as PopoverTitle, M as addNodeIntent, Mt as Popover, N as deriveAddNodeOptions, Nt as PopoverContent, On as connection, Ot as updateTriggerSchedule, Qt as EmptyTitle, Rt as PopoverTrigger, St as resetInputValues, Tn as isJsonObject, Tt as setInputSources, U as resourceValue, Un as string, Vn as jsonValue, W as designerGraph, Wn as ApiError, Wt as InputGroup, Xt as EmptyHeader, Yt as EmptyDescription, Zt as EmptyMedia, _ as updateValue, _n as randomId, _t as setComment, a as nameCreatedNodes, at as deriveInspectorDiagnostics, bn as decodeTriggerCatalog, bt as setNodePositions, c as updateCondition, d as updateNodeName, dt as connectionCatalog, f as updateNodeSettings, ft as canvasPresentationChange, g as updateTaskPorts, gn as WorkbenchClient, gt as restoreCanvasPresentation, h as updateTaskAdditionalInputs, hn as errorNotice, ht as removeComments, i as deleteSelection, it as Latest, jt as Switch, kn as connectorActionMetadata, kt as IdTooltip, l as updateNodeDescription, m as updateTask, mt as flowPresentation, nn as createI18n, nt as revisionView, o as setInputValue, ot as diagnosticItems, p as updateResolution, pt as commentIds, qn as IconThemeContext, qt as Empty, r as applyFlowChanges, rt as flowResourceReferences, s as setInputVariable, t as addNode, u as updateNodeIcon, ut as actionWithConnections, v as updateWebhook, vn as allConnectorConnectionsQuery, vt as setFlowViewport, wn as triggerOutputDefinitions, wt as setConnectorConnection, xt as repairNodeNames, yt as setNodeContentHidden, z as scopedValue, zn as inputValue } from "./flowChanges-fBiAeTzR.js";
1
+ import { $ as RunStore, $t as EventSourcesPage, An as providerIconAppearance, At as followWorkbenchLink, B as CacheResource, Bn as invalidResponse, Cn as sampleErrorOutputs, Ct as setCodeActions, Dt as updateTriggerConfig, Et as setTriggerConnection, F as feishuResourceKind, Gn as controlErrorCode, Gt as InputGroupAddon, H as resourceData, Hn as record, I as supportsFeishuChatFilter, J as providerIcon, Jt as EmptyContent, Kt as InputGroupInput, L as combineSources, Lt as PopoverTitle, M as addNodeIntent, Mt as Popover, N as deriveAddNodeOptions, Nt as PopoverContent, On as connection, Ot as updateTriggerSchedule, Qt as EmptyTitle, Rt as PopoverTrigger, St as resetInputValues, Tn as isJsonObject, Tt as setInputSources, U as resourceValue, Un as string, Vn as jsonValue, W as designerGraph, Wn as ApiError, Wt as InputGroup, Xt as EmptyHeader, Yt as EmptyDescription, Zt as EmptyMedia, _ as updateValue, _n as randomId, _t as setComment, a as nameCreatedNodes, at as deriveInspectorDiagnostics, bn as decodeTriggerCatalog, bt as setNodePositions, c as updateCondition, d as updateNodeName, dt as connectionCatalog, f as updateNodeSettings, ft as canvasPresentationChange, g as updateTaskPorts, gn as WorkbenchClient, gt as restoreCanvasPresentation, h as updateTaskAdditionalInputs, hn as errorNotice, ht as removeComments, i as deleteSelection, it as Latest, jt as Switch, kn as connectorActionMetadata, kt as IdTooltip, l as updateNodeDescription, m as updateTask, mt as flowPresentation, nn as createI18n, nt as revisionView, o as setInputValue, ot as diagnosticItems, p as updateResolution, pt as commentIds, qn as IconThemeContext, qt as Empty, r as applyFlowChanges, rt as flowResourceReferences, s as setInputVariable, t as addNode, u as updateNodeIcon, ut as actionWithConnections, v as updateWebhook, vn as allConnectorConnectionsQuery, vt as setFlowViewport, wn as triggerOutputDefinitions, wt as setConnectorConnection, xt as repairNodeNames, yt as setNodeContentHidden, z as scopedValue, zn as inputValue } from "./flowChanges-DL0Njlg6.js";
2
2
  import { a as derive, i as compute, l as val } from "./dist-bO8nvHFH.js";
3
3
  import { a as toPlainObject, c as useLang, l as useTranslate, o as I18nProvider, u as useVal } from "./dist-BFmpkX4P.js";
4
4
  import { E as FieldLabel, H as applyFlowChanges$1, J as resourceNameIssue, Q as nodeInputMappings, S as Field, T as FieldGroup, V as FlowChangeError, Z as mapConditionSources, a as DialogDescription, at as dequal$1, bi as cn, c as DialogTitle, d as TooltipContent, i as DialogContent, n as Dialog, o as DialogFooter, or as Input, p as TooltipTrigger, r as DialogClose, s as DialogHeader, t as Icon, u as Tooltip, vi as Button, w as FieldError, x as Spinner } from "./icons-6oQ8Q9Wm.js";
@@ -6735,7 +6735,7 @@ var WorkbenchStore = class {
6735
6735
  };
6736
6736
  //#endregion
6737
6737
  //#region src/workbench/browser/runtime/openFlowWorkbench.tsx
6738
- var FlowWorkspace = lazy(() => import("./flowWorkspace-MqcEfg7T.js"));
6738
+ var FlowWorkspace = lazy(() => import("./flowWorkspace-Cp5CcEGu.js"));
6739
6739
  function OpenFlowSessionGate({ action, description, error, onSubmit, onTokenChange, pending = false, title, token, tokenLabel }) {
6740
6740
  const hasToken = token != null && tokenLabel != null && onTokenChange != null;
6741
6741
  const tokenValue = token ?? "";
@@ -14062,7 +14062,7 @@ var schemas = {
14062
14062
  comment: waitCommentSchema,
14063
14063
  version
14064
14064
  }),
14065
- putVariable: strictObject({ value: string().refine((value) => new TextEncoder().encode(value).byteLength <= 65536, "Environment variable value is too large.") }),
14065
+ putVariable: strictObject({ value: string().refine((value) => new TextEncoder().encode(value).byteLength <= 65536, "Variable value is too large.") }),
14066
14066
  queryConnectorAccessCandidates: strictObject({
14067
14067
  providerIds: array(id.max(256)).min(1).refine((ids) => new Set(ids).size == ids.length, "Duplicate provider IDs."),
14068
14068
  version
@@ -0,0 +1,39 @@
1
+ import type { JsonValue } from '../browser/flow-change.js';
2
+ export interface SourceConfiguration {
3
+ readonly identity: string;
4
+ readonly eventTypes: string[];
5
+ readonly config: Readonly<Record<string, JsonValue>>;
6
+ readonly secrets: Readonly<Record<string, JsonValue>>;
7
+ readonly state: Readonly<Record<string, JsonValue>>;
8
+ }
9
+ export interface SourceEvent {
10
+ readonly id: string;
11
+ readonly type: string;
12
+ readonly payload: Readonly<Record<string, JsonValue>>;
13
+ }
14
+ export interface SourceSubscription {
15
+ readonly key: string;
16
+ readonly config: Readonly<Record<string, JsonValue>>;
17
+ }
18
+ export interface EventSourceDefinition<Create, Update, View> {
19
+ readonly kind: string;
20
+ readonly provider: string;
21
+ readonly maximumBodyBytes: number;
22
+ readonly initialize: (input: Create, accountId: string | undefined) => SourceConfiguration;
23
+ readonly update: (source: SourceConfiguration, input: Update) => SourceConfiguration;
24
+ readonly view: (source: SourceConfiguration) => View;
25
+ readonly ready: (source: SourceConfiguration) => boolean;
26
+ readonly receive: (source: SourceConfiguration, bytes: Uint8Array, headers: Headers, now: number) => Promise<{
27
+ readonly response: Response;
28
+ readonly state: SourceConfiguration['state'];
29
+ } | {
30
+ readonly event: SourceEvent;
31
+ }>;
32
+ readonly matches: (config: Readonly<Record<string, JsonValue>>, event: SourceEvent) => boolean;
33
+ readonly resources: {
34
+ readonly triggerId: string;
35
+ readonly requests: (source: SourceConfiguration, provider: string, config: Readonly<Record<string, JsonValue>>) => readonly SourceSubscription[];
36
+ };
37
+ }
38
+ export declare class SourceIdentityError extends Error {
39
+ }
@@ -0,0 +1,3 @@
1
+ import type { CreateEventSource, EventSource, UpdateEventSource } from './event-sources.js';
2
+ import type { EventSourceDefinition } from './event-source-definition.js';
3
+ export declare const feishuApplication: EventSourceDefinition<CreateEventSource, UpdateEventSource, Pick<EventSource, 'appId' | 'manageSubscriptions' | 'verificationTokenConfigured' | 'encryptKeyConfigured' | 'verifiedAt'>>;
@@ -0,0 +1,6 @@
1
+ import { feishuApplication } from './event-source-feishu.js';
2
+ export type { EventSourceDefinition, SourceConfiguration, SourceEvent, SourceSubscription } from './event-source-definition.js';
3
+ export { SourceIdentityError } from './event-source-definition.js';
4
+ export { feishuApplication } from './event-source-feishu.js';
5
+ declare const definitions: readonly (typeof feishuApplication)[];
6
+ export declare function eventSourceDefinition(kind: string): (typeof definitions)[number];
@@ -0,0 +1,289 @@
1
+ import { PermanentIntegrationError } from "./integration-trigger.js";
2
+ //#region src/base/common/json.ts
3
+ function hasJsonShape(root) {
4
+ if (root === null || typeof root == "boolean" || typeof root == "string") return true;
5
+ if (typeof root == "number") return Number.isFinite(root);
6
+ if (typeof root != "object") return false;
7
+ const ancestors = /* @__PURE__ */ new Set();
8
+ const pending = [];
9
+ let value = root;
10
+ traversal: while (true) {
11
+ let keys;
12
+ let arrayLength;
13
+ if (Array.isArray(value)) {
14
+ arrayLength = value.length;
15
+ keys = Reflect.ownKeys(value);
16
+ if (keys.length != arrayLength + 1) return false;
17
+ } else {
18
+ const prototype = Reflect.getPrototypeOf(value);
19
+ if (prototype != Object.prototype && prototype != null) return false;
20
+ keys = Reflect.ownKeys(value);
21
+ arrayLength = void 0;
22
+ }
23
+ ancestors.add(value);
24
+ let index = 0;
25
+ while (true) {
26
+ if (index >= keys.length) {
27
+ ancestors.delete(value);
28
+ const parent = pending.pop();
29
+ if (parent === void 0) return true;
30
+ value = parent.value;
31
+ keys = parent.keys;
32
+ arrayLength = parent.arrayLength;
33
+ index = parent.index;
34
+ continue;
35
+ }
36
+ const key = keys[index++];
37
+ if (arrayLength !== void 0) {
38
+ if (key == "length") continue;
39
+ if (typeof key != "string") return false;
40
+ const arrayIndex = Number(key);
41
+ if (!Number.isSafeInteger(arrayIndex) || arrayIndex < 0 || arrayIndex >= arrayLength || String(arrayIndex) != key) return false;
42
+ } else if (typeof key != "string") return false;
43
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
44
+ if (descriptor?.enumerable != true || !Object.hasOwn(descriptor, "value")) return false;
45
+ const child = descriptor.value;
46
+ if (child === null || typeof child == "boolean" || typeof child == "string") continue;
47
+ if (typeof child == "number") {
48
+ if (!Number.isFinite(child)) return false;
49
+ continue;
50
+ }
51
+ if (typeof child != "object" || ancestors.has(child)) return false;
52
+ pending.push({
53
+ value,
54
+ keys,
55
+ arrayLength,
56
+ index
57
+ });
58
+ value = child;
59
+ continue traversal;
60
+ }
61
+ }
62
+ }
63
+ function isJsonObject(value) {
64
+ try {
65
+ return value != null && typeof value == "object" && !Array.isArray(value) && hasJsonShape(value);
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+ //#endregion
71
+ //#region src/trigger/providers/feishu/events.ts
72
+ var encoder = new TextEncoder();
73
+ var decoder = new TextDecoder("utf-8", { fatal: true });
74
+ async function receiveFeishuEvent(rawBody, headers, source, now) {
75
+ const envelope = JSON.parse(decoder.decode(rawBody));
76
+ if (!isJsonObject(envelope) || typeof envelope.encrypt != "string" || !/^[A-Za-z0-9+/]+={0,2}$/.test(envelope.encrypt)) throw new TypeError("An encrypted Feishu envelope is required.");
77
+ const bytes = Uint8Array.from(atob(envelope.encrypt), (character) => character.charCodeAt(0));
78
+ if (bytes.length < 32 || bytes.length % 16 != 0) throw new TypeError("Invalid encrypted Feishu envelope.");
79
+ const key = await crypto.subtle.importKey("raw", await crypto.subtle.digest("SHA-256", encoder.encode(source.encryptKey)), "AES-CBC", false, ["decrypt"]);
80
+ const value = JSON.parse(decoder.decode(await crypto.subtle.decrypt({
81
+ name: "AES-CBC",
82
+ iv: bytes.slice(0, 16)
83
+ }, key, bytes.slice(16))));
84
+ if (!isJsonObject(value)) throw new TypeError("Invalid Feishu event.");
85
+ const header = isJsonObject(value.header) ? value.header : void 0;
86
+ if (!sameSecret(header?.token ?? value.token, source.verificationToken)) throw new TypeError("Invalid Feishu verification token.");
87
+ if (value.type == "url_verification") {
88
+ if (typeof value.challenge != "string" || value.challenge.length == 0 || value.challenge.length > 1024) throw new TypeError("Invalid challenge.");
89
+ return { challenge: value.challenge };
90
+ }
91
+ const timestamp = headers.get("x-lark-request-timestamp") ?? "";
92
+ const nonce = headers.get("x-lark-request-nonce") ?? "";
93
+ const signature = headers.get("x-lark-signature") ?? "";
94
+ if (!/^\d{10}$/.test(timestamp) || Math.abs(now - Number(timestamp) * 1e3) > 864e5 || nonce.length == 0 || nonce.length > 256) throw new TypeError("Invalid Feishu request identity.");
95
+ const prefix = encoder.encode(timestamp + nonce + source.encryptKey);
96
+ const signed = new Uint8Array(prefix.length + rawBody.length);
97
+ signed.set(prefix);
98
+ signed.set(rawBody, prefix.length);
99
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", signed));
100
+ if (!sameSecret(signature, Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join(""))) throw new TypeError("Invalid Feishu signature.");
101
+ const body = value.event;
102
+ if (!isJsonObject(body)) throw new TypeError("Missing Feishu event body.");
103
+ if (value.schema != null && value.schema != "2.0") throw new TypeError("Unsupported Feishu event schema.");
104
+ const type = header?.event_type ?? body.type;
105
+ const appId = header?.app_id ?? body.app_id ?? value.app_id;
106
+ const tenantKey = header?.tenant_key ?? body.tenant_key ?? value.tenant_key;
107
+ let id = header?.event_id ?? value.uuid;
108
+ if (type == "im.message.receive_v1" && isJsonObject(body.message)) id = body.message.message_id;
109
+ if (typeof id != "string" || id.length == 0 || id.length > 256 || typeof type != "string" || !/^[a-z][a-z0-9_.]{0,127}$/.test(type)) throw new TypeError("Missing Feishu event identity.");
110
+ if (appId != source.appId) throw new TypeError("Feishu event source does not match.");
111
+ if (typeof tenantKey != "string" || tenantKey.length == 0 || tenantKey.length > 256) throw new TypeError("Invalid Feishu event tenant.");
112
+ if (type == "card.action.trigger" || type == "app_ticket") throw new TypeError("This callback is not an asynchronous business event.");
113
+ const { token: _token, ...business } = body;
114
+ const occurredAt = header?.create_time ?? value.ts;
115
+ return { event: {
116
+ id: `${type}:${id}`,
117
+ type,
118
+ appId: source.appId,
119
+ tenantKey,
120
+ occurredAt: typeof occurredAt == "string" ? occurredAt : null,
121
+ body: business
122
+ } };
123
+ }
124
+ function sameSecret(value, expected) {
125
+ if (typeof value != "string" || value.length != expected.length) return false;
126
+ let difference = 0;
127
+ for (let index = 0; index < expected.length; index += 1) difference |= value.charCodeAt(index) ^ expected.charCodeAt(index);
128
+ return difference == 0;
129
+ }
130
+ function matchesFeishuEvent(config, event) {
131
+ if (!Array.isArray(config.eventTypes) || !config.eventTypes.includes(event.type)) return false;
132
+ const chatIds = config.chatIds;
133
+ if (Array.isArray(chatIds) && chatIds.length > 0) {
134
+ const chatId = isJsonObject(event.body.message) ? event.body.message.chat_id : event.body.chat_id;
135
+ if (typeof chatId != "string" || !chatIds.includes(chatId)) return false;
136
+ }
137
+ const resource = config.resource;
138
+ if (isJsonObject(resource)) {
139
+ if ((resource.kind == "document" ? event.body.file_token : resource.kind == "calendar" ? event.body.calendar_id : event.body.approval_code) != resource.id) return false;
140
+ }
141
+ return true;
142
+ }
143
+ //#endregion
144
+ //#region src/trigger/providers/feishu/config.ts
145
+ function feishuResourceKind(events) {
146
+ if (events.length == 0) return;
147
+ if (events.every((event) => event.startsWith("drive.file."))) return "document";
148
+ if (events.every((event) => event == "calendar.calendar.event.changed_v4")) return "calendar";
149
+ if (events.every((event) => [
150
+ "approval_instance",
151
+ "approval_task",
152
+ "approval_cc",
153
+ "approval"
154
+ ].includes(event))) return "approval";
155
+ }
156
+ //#endregion
157
+ //#region src/trigger/providers/feishu/subscriptions.ts
158
+ function feishuSubscriptions(config, provider) {
159
+ if (config.resource == null) return [];
160
+ if (!isJsonObject(config.resource)) throw new PermanentIntegrationError("Invalid Feishu resource.");
161
+ const { kind, id, documentType } = config.resource;
162
+ if (typeof id != "string" || id.length == 0 || kind != feishuResourceKind(config.eventTypes)) throw new PermanentIntegrationError("Invalid resource subscription.");
163
+ if (kind == "document") {
164
+ if (typeof documentType != "string" || ![
165
+ "doc",
166
+ "docx",
167
+ "sheet",
168
+ "bitable",
169
+ "file",
170
+ "folder",
171
+ "slides"
172
+ ].includes(documentType)) throw new PermanentIntegrationError("A supported document type is required.");
173
+ return config.eventTypes.map((eventType) => ({
174
+ key: JSON.stringify([
175
+ "document",
176
+ documentType,
177
+ id,
178
+ eventType
179
+ ]),
180
+ config: {
181
+ ...config,
182
+ eventTypes: [eventType]
183
+ }
184
+ }));
185
+ }
186
+ if (kind == "calendar" || kind == "approval" && provider == "feishu_app_bot") return [{
187
+ key: JSON.stringify([kind, id]),
188
+ config
189
+ }];
190
+ throw new PermanentIntegrationError("Unsupported Feishu resource subscription.");
191
+ }
192
+ //#endregion
193
+ //#region src/event-source/common/definition.ts
194
+ var SourceIdentityError = class extends Error {};
195
+ //#endregion
196
+ //#region src/event-source/common/feishu.ts
197
+ var feishuApplication = {
198
+ kind: "feishu",
199
+ provider: "feishu_app_bot",
200
+ maximumBodyBytes: 65536,
201
+ initialize(input, accountId) {
202
+ if (accountId == null || !/^cli_[a-zA-Z0-9]+$/.test(accountId)) throw new SourceIdentityError("Connector must provide the verified application identity.");
203
+ return {
204
+ identity: accountId,
205
+ eventTypes: input.eventTypes,
206
+ config: { manageSubscriptions: input.manageSubscriptions },
207
+ secrets: {
208
+ verificationToken: input.verificationToken,
209
+ encryptKey: input.encryptKey
210
+ },
211
+ state: {}
212
+ };
213
+ },
214
+ update(source, input) {
215
+ const secrets = {
216
+ verificationToken: input.verificationToken ?? source.secrets.verificationToken,
217
+ encryptKey: input.encryptKey ?? source.secrets.encryptKey
218
+ };
219
+ const changed = secrets.verificationToken != source.secrets.verificationToken || secrets.encryptKey != source.secrets.encryptKey;
220
+ return {
221
+ identity: source.identity,
222
+ eventTypes: input.eventTypes,
223
+ config: source.config,
224
+ secrets,
225
+ state: changed ? {} : source.state
226
+ };
227
+ },
228
+ view(source) {
229
+ return {
230
+ appId: source.identity,
231
+ manageSubscriptions: source.config.manageSubscriptions === true,
232
+ verificationTokenConfigured: true,
233
+ encryptKeyConfigured: true,
234
+ verifiedAt: typeof source.state.verifiedAt == "number" ? new Date(source.state.verifiedAt).toISOString() : null
235
+ };
236
+ },
237
+ ready: (source) => typeof source.state.verifiedAt == "number",
238
+ async receive(source, bytes, headers, now) {
239
+ const received = await receiveFeishuEvent(bytes, headers, {
240
+ appId: source.identity,
241
+ verificationToken: source.secrets.verificationToken,
242
+ encryptKey: source.secrets.encryptKey
243
+ }, now);
244
+ if ("challenge" in received) return {
245
+ response: Response.json(received),
246
+ state: {
247
+ ...source.state,
248
+ verifiedAt: now
249
+ }
250
+ };
251
+ const event = received.event;
252
+ return { event: {
253
+ id: event.id,
254
+ type: event.type,
255
+ payload: {
256
+ event: event.type,
257
+ deliveryId: event.id,
258
+ appId: event.appId,
259
+ tenantKey: event.tenantKey,
260
+ occurredAt: event.occurredAt,
261
+ body: event.body
262
+ }
263
+ } };
264
+ },
265
+ matches(config, event) {
266
+ return matchesFeishuEvent(config, {
267
+ type: event.type,
268
+ body: event.payload.body
269
+ });
270
+ },
271
+ resources: {
272
+ triggerId: "feishu_app_bot.on_event",
273
+ requests(source, provider, config) {
274
+ const subscriptions = feishuSubscriptions(config, provider);
275
+ if (subscriptions.length > 0 && source.config.manageSubscriptions !== true) throw new PermanentIntegrationError("This source does not manage resource subscriptions.");
276
+ return subscriptions;
277
+ }
278
+ }
279
+ };
280
+ //#endregion
281
+ //#region src/event-source/common/index.ts
282
+ var definitions = [feishuApplication];
283
+ function eventSourceDefinition(kind) {
284
+ const definition = definitions.find((item) => item.kind == kind);
285
+ if (definition == null) throw new Error(`Unknown event source kind: ${kind}`);
286
+ return definition;
287
+ }
288
+ //#endregion
289
+ export { SourceIdentityError, eventSourceDefinition, feishuApplication };
@@ -16,4 +16,4 @@ export declare function receiveFeishuEvent(rawBody: Uint8Array, headers: Headers
16
16
  } | {
17
17
  readonly event: FeishuEvent;
18
18
  }>;
19
- export declare function matchesFeishuEvent(config: Readonly<Record<string, JsonValue>>, event: FeishuEvent): boolean;
19
+ export declare function matchesFeishuEvent(config: Readonly<Record<string, JsonValue>>, event: Pick<FeishuEvent, 'type' | 'body'>): boolean;
@@ -12052,8 +12052,8 @@ function checkSource(source, graph, document, targetInput, path, diagnostics) {
12052
12052
  switch (source.kind) {
12053
12053
  case "binding":
12054
12054
  if (document.bindings[source.bindingId] == null) diagnostics.push(graphDiagnostic("graph.binding-missing", `Binding "${source.bindingId}" does not exist.`, path, { bindingId: source.bindingId }));
12055
- else if (document.bindings[source.bindingId].kind != "variable") diagnostics.push(graphDiagnostic("graph.binding-invalid", `Binding "${source.bindingId}" must be an environment variable.`, path, { bindingId: source.bindingId }));
12056
- else if (targetInput != null && !variableInputCompatible(targetInput.jsonSchema)) diagnostics.push(graphDiagnostic("graph.variable-incompatible", `Environment variable binding "${source.bindingId}" is not compatible with this input.`, path, { bindingId: source.bindingId }));
12055
+ else if (document.bindings[source.bindingId].kind != "variable") diagnostics.push(graphDiagnostic("graph.binding-invalid", `Binding "${source.bindingId}" must be a variable.`, path, { bindingId: source.bindingId }));
12056
+ else if (targetInput != null && !variableInputCompatible(targetInput.jsonSchema)) diagnostics.push(graphDiagnostic("graph.variable-incompatible", `Variable binding "${source.bindingId}" is not compatible with this input.`, path, { bindingId: source.bindingId }));
12057
12057
  return;
12058
12058
  case "node": {
12059
12059
  const upstream = graph.nodes[source.nodeId];
@@ -12302,7 +12302,7 @@ function validateGraph(graph, document, path, diagnostics) {
12302
12302
  }));
12303
12303
  if (mapping.kind == "sources") {
12304
12304
  const variableSources = mapping.sources.filter((source) => source.kind == "binding" && document.bindings[source.bindingId]?.kind == "variable");
12305
- if (variableSources.length > 0 && (variableSources.length != 1 || mapping.sources.length != 1)) diagnostics.push(graphDiagnostic("graph.variable-source-mixed", "An environment variable must be the only source for an input.", mappingPath));
12305
+ if (variableSources.length > 0 && (variableSources.length != 1 || mapping.sources.length != 1)) diagnostics.push(graphDiagnostic("graph.variable-source-mixed", "A variable must be the only source for an input.", mappingPath));
12306
12306
  for (const source of mapping.sources) checkSource(source, graph, document, inputPorts[handle], mappingPath, diagnostics);
12307
12307
  }
12308
12308
  }
@@ -12345,7 +12345,7 @@ function validateGraph(graph, document, path, diagnostics) {
12345
12345
  }
12346
12346
  function validateFlowGraph(revision) {
12347
12347
  const diagnostics = [];
12348
- for (const [bindingId, binding] of Object.entries(revision.document.bindings)) if (binding.kind == "variable" && !validVariableName(binding.target)) diagnostics.push(graphDiagnostic("binding.variable-invalid", `Environment variable binding "${bindingId}" has an invalid target.`, `/document/bindings/${bindingId}/target`, { bindingId }));
12348
+ for (const [bindingId, binding] of Object.entries(revision.document.bindings)) if (binding.kind == "variable" && !validVariableName(binding.target)) diagnostics.push(graphDiagnostic("binding.variable-invalid", `Variable binding "${bindingId}" has an invalid target.`, `/document/bindings/${bindingId}/target`, { bindingId }));
12349
12349
  validateGraph(revision.document.graph, revision.document, "/document/graph", diagnostics);
12350
12350
  return diagnostics;
12351
12351
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oomol-lab/open-flow",
3
- "version": "0.1.0-beta.57",
3
+ "version": "0.1.0-beta.59",
4
4
  "description": "TypeScript contracts and Browser runtime for Open Flow.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -53,6 +53,10 @@
53
53
  "types": "./dist/common/cron-trigger.d.ts",
54
54
  "import": "./dist/common/cron-trigger.js"
55
55
  },
56
+ "./event-source": {
57
+ "types": "./dist/common/event-source.d.ts",
58
+ "import": "./dist/common/event-source.js"
59
+ },
56
60
  "./integration-trigger": {
57
61
  "types": "./dist/common/integration-trigger.d.ts",
58
62
  "import": "./dist/common/integration-trigger.js"