@powerhousedao/reactor-workflow 6.2.3-dev.11
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/LICENSE +661 -0
- package/README.md +130 -0
- package/dist/descriptor-DXbWuxhE.js +169 -0
- package/dist/descriptor-DXbWuxhE.js.map +1 -0
- package/dist/index.d.ts +19068 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3771 -0
- package/dist/index.js.map +1 -0
- package/dist/piece-registry-BWWihLyp.js +1419 -0
- package/dist/piece-registry-BWWihLyp.js.map +1 -0
- package/dist/piece-registry-CE0UFmDA.d.ts +600 -0
- package/dist/piece-registry-CE0UFmDA.d.ts.map +1 -0
- package/dist/redact-C7LWgAyD.js +797 -0
- package/dist/redact-C7LWgAyD.js.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +4 -0
- package/dist/worker-entry.d.ts +1 -0
- package/dist/worker-entry.js +605 -0
- package/dist/worker-entry.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @powerhousedao/reactor-workflow
|
|
2
|
+
|
|
3
|
+
The workflow engine that runs on a reactor. It owns the runtime that turns a
|
|
4
|
+
`powerhouse/workflow` document into runs — triggers, the run journal, managed
|
|
5
|
+
secrets, connections — and, beneath it, the machinery that runs an Activepieces
|
|
6
|
+
piece in a child process.
|
|
7
|
+
|
|
8
|
+
The document models, their editors and Workflow Studio live in
|
|
9
|
+
[`@powerhousedao/workflow`](../workflow); the GraphQL subgraph that serves this
|
|
10
|
+
runtime lives in [`apps/switchboard`](../../apps/switchboard), the host that
|
|
11
|
+
composes the runtime.
|
|
12
|
+
|
|
13
|
+
## The seam
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
src/pieces/ loader, descriptor, worker pool, egress, executor, expressions
|
|
17
|
+
src/reactor/ trigger supervisor, coordinator, run journal, secret store, ports
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`src/pieces` runs a piece. It knows nothing about reactors, documents or
|
|
21
|
+
Powerhouse packages: give it a piece name, a config and a connection value and
|
|
22
|
+
it returns an output. The boundary is enforced by lint — nothing under
|
|
23
|
+
`src/pieces` may import `src/reactor`, nor any `@powerhousedao/*` package other
|
|
24
|
+
than `@powerhousedao/pieces-framework`, whose contract it implements — so the
|
|
25
|
+
piece layer stays something you can reason about on its own.
|
|
26
|
+
|
|
27
|
+
`src/reactor` is everything that only makes sense on a reactor: which workflow a
|
|
28
|
+
trigger belongs to, where a run is journaled, whose credentials a step may
|
|
29
|
+
resolve. It depends on the piece layer, never the other way round.
|
|
30
|
+
|
|
31
|
+
## What comes from the piece framework
|
|
32
|
+
|
|
33
|
+
The contract this engine implements is declared once, in
|
|
34
|
+
[`@powerhousedao/pieces-framework`](../pieces-framework), and taken from there
|
|
35
|
+
rather than restated here.
|
|
36
|
+
|
|
37
|
+
- The **types**. `ApAction`, `ApTrigger`, `ApPiece` and `ApProperty` derive from
|
|
38
|
+
`ActionBase`, `TriggerBase`, `PieceBase` and the property schemas; the
|
|
39
|
+
contexts from `Store`, `ServerContext`, `FilesService`, `ConnectionsManager`,
|
|
40
|
+
`FlowsContext`, `RunContext`, `TriggerHookContext` and `SetScheduleRequest`;
|
|
41
|
+
the connection shapes from `AppConnectionType` and `AppConnectionValue`.
|
|
42
|
+
`PackagePiece`, `ReactorService` and `DEDUPE_KEY_PROPERTY` are the framework's
|
|
43
|
+
own Powerhouse half.
|
|
44
|
+
- The **enums stay strings here**. A piece bundle inlines its own copy of the
|
|
45
|
+
framework, so a `PropertyType` or `TriggerStrategy` read off one shares no
|
|
46
|
+
identity with ours. Every such value is compared as a string; nothing in
|
|
47
|
+
`src/pieces` uses `instanceof` or enum identity across that boundary.
|
|
48
|
+
- **Prop coercion**, from `@powerhousedao/pieces-framework/host`, which carries
|
|
49
|
+
the Activepieces engine's own property processors. `context/normalize.ts`
|
|
50
|
+
dispatches to them; only file props stay ours, because attachment and
|
|
51
|
+
`apfile://` refs, the size ceiling and a host-injected fetcher have no
|
|
52
|
+
upstream equivalent. An `ApFile` a processor builds is flattened to a plain
|
|
53
|
+
object at that boundary: a class instance does not survive the worker IPC.
|
|
54
|
+
- **The SSRF table**, likewise from `./host`. `worker/egress.ts` classifies an
|
|
55
|
+
address with `ssrfIpClassifier.isBlockedIp`; the connect-time socket and DNS
|
|
56
|
+
hooks, the per-request policy and the allow-lists are ours. The one range the
|
|
57
|
+
classifier reads as unicast and we still refuse is the deprecated
|
|
58
|
+
IPv4-compatible `::/96` block, which carries the metadata endpoint.
|
|
59
|
+
- **Error formatting**, again from `./host`. A thrown piece error passes through
|
|
60
|
+
`formatPieceError` before redaction, so the HTTP status, request, response and
|
|
61
|
+
the text of an HTML error page reach the run journal. Redaction runs last,
|
|
62
|
+
over the formatter's output as well.
|
|
63
|
+
|
|
64
|
+
## How the host composes it
|
|
65
|
+
|
|
66
|
+
The engine names no host type. `WorkflowRuntimeHostDeps` (`src/reactor/host.ts`)
|
|
67
|
+
is what the runtime reads — a relational db, a reactor client, the read and
|
|
68
|
+
write checks, and optionally the HTTP scope its webhook endpoints live under.
|
|
69
|
+
`createWorkflowRuntime(deps)` returns a configured runtime; nothing here
|
|
70
|
+
constructs one by itself.
|
|
71
|
+
|
|
72
|
+
Switchboard is that host (`apps/switchboard/src/workflow-runtime.mts`). It
|
|
73
|
+
resolves the `workflows` flag, builds the runtime from what `startAPI` hands
|
|
74
|
+
back, and owns the GraphQL face in `apps/switchboard/src/workflow/`, registered
|
|
75
|
+
live with the GraphQL manager the way a package subgraph is. reactor-api knows
|
|
76
|
+
nothing about workflows.
|
|
77
|
+
|
|
78
|
+
Document operations reach the runtime through a **read model**, not a
|
|
79
|
+
processor: `WorkflowTriggersReadModel` (`WORKFLOW_TRIGGERS_READ_MODEL`) is
|
|
80
|
+
registered on the reactor's read-model coordinator at the
|
|
81
|
+
`WORKFLOW_TRIGGERS_READ_MODEL_STAGE` (`post_ready`) stage, and its
|
|
82
|
+
`indexOperations` is `runtime.onOperations`. The durable cursor `BaseReadModel`
|
|
83
|
+
gives it means an operation written while the runtime is down catches up on the
|
|
84
|
+
next boot instead of vanishing; a fresh registration starts at head, so history
|
|
85
|
+
is never replayed. `onOperations` journals a matched fire before it returns, so
|
|
86
|
+
the cursor never passes an event that is not yet durable.
|
|
87
|
+
|
|
88
|
+
The webhook endpoints are registered under the `@powerhousedao/workflow`
|
|
89
|
+
namespace, which this package exports as `WORKFLOW_PACKAGE_NAME`.
|
|
90
|
+
|
|
91
|
+
## The worker entry
|
|
92
|
+
|
|
93
|
+
A piece runs in a forked node child, never in the reactor's process. The
|
|
94
|
+
transport finds that child's code by walking up to this package's own
|
|
95
|
+
`package.json` and reading `dist/worker-entry.js`, so `pnpm build` must have run
|
|
96
|
+
before anything executes a piece — including the suites here.
|
|
97
|
+
|
|
98
|
+
## `./testing`
|
|
99
|
+
|
|
100
|
+
`@powerhousedao/reactor-workflow/testing` re-exports the piece layer: the
|
|
101
|
+
loader, the worker and its pool, the executor, the coordinator, and the
|
|
102
|
+
in-memory secret and connection resolvers. It is what a piece author's own
|
|
103
|
+
package uses to run its piece the way this reactor will, without standing up a
|
|
104
|
+
reactor to do it.
|
|
105
|
+
|
|
106
|
+
## Running the tests
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
pnpm build # dist/worker-entry.js, which the piece suites fork
|
|
110
|
+
pnpm test
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Bundles fetched from npm are cached in `node_modules/.cache/ap-bundles`; the
|
|
114
|
+
suites that need one skip when it cannot be fetched, so the offline run is
|
|
115
|
+
smaller but green. Two suites need the docling piece's own package checked out
|
|
116
|
+
beside this repo, and skip otherwise.
|
|
117
|
+
|
|
118
|
+
Environment:
|
|
119
|
+
|
|
120
|
+
- `PH_SECRETS_MASTER_KEY` — 32 bytes of hex for the managed secret store. Unset,
|
|
121
|
+
it generates and reuses a key file.
|
|
122
|
+
- `WORKFLOW_EGRESS_ALLOW_ADDRESSES` — comma-separated CIDRs a piece may reach.
|
|
123
|
+
The default policy refuses private and loopback addresses; a suite that talks
|
|
124
|
+
to a local mock service widens it.
|
|
125
|
+
|
|
126
|
+
## Design documents
|
|
127
|
+
|
|
128
|
+
[`docs/plan`](./docs/plan) holds the specifications this engine was built from —
|
|
129
|
+
the automation spec (08), the piece-loading architecture (06), the secrets
|
|
130
|
+
service (09) and the HTTP routes (10) are the ones worth reading first.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { R as getActions, z as getTriggers } from "./redact-C7LWgAyD.js";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
//#region src/pieces/activepieces/loader.ts
|
|
7
|
+
function resolveEntry(pieceDir) {
|
|
8
|
+
const raw = readFileSync(path.join(pieceDir, "package.json"), "utf8");
|
|
9
|
+
const pkg = JSON.parse(raw);
|
|
10
|
+
const dotExport = pkg.exports?.["."];
|
|
11
|
+
const candidates = [];
|
|
12
|
+
if (typeof dotExport === "string") candidates.push(dotExport);
|
|
13
|
+
if (dotExport && typeof dotExport === "object") {
|
|
14
|
+
const cond = dotExport;
|
|
15
|
+
for (const key of [
|
|
16
|
+
"import",
|
|
17
|
+
"require",
|
|
18
|
+
"default"
|
|
19
|
+
]) {
|
|
20
|
+
const value = cond[key];
|
|
21
|
+
if (typeof value === "string") candidates.push(value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
candidates.push(pkg.main, pkg.module, "src/index.js", "index.js", "main.js");
|
|
25
|
+
const root = realpathSync(pieceDir);
|
|
26
|
+
const rootWithSep = root + path.sep;
|
|
27
|
+
for (const candidate of candidates) {
|
|
28
|
+
if (!candidate) continue;
|
|
29
|
+
const abs = path.resolve(pieceDir, candidate);
|
|
30
|
+
let real;
|
|
31
|
+
try {
|
|
32
|
+
real = realpathSync(abs);
|
|
33
|
+
} catch {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (real !== root && !real.startsWith(rootWithSep)) continue;
|
|
37
|
+
return abs;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`No entry file found for piece bundle at ${pieceDir}`);
|
|
40
|
+
}
|
|
41
|
+
function isRecord(value) {
|
|
42
|
+
return typeof value === "object" && value !== null;
|
|
43
|
+
}
|
|
44
|
+
function findPiece(mod) {
|
|
45
|
+
const candidates = [
|
|
46
|
+
...Object.values(mod),
|
|
47
|
+
...isRecord(mod.default) ? Object.values(mod.default) : [],
|
|
48
|
+
mod.default
|
|
49
|
+
];
|
|
50
|
+
for (const candidate of candidates) if (isRecord(candidate) && candidate.constructor.name === "Piece") return {
|
|
51
|
+
piece: candidate,
|
|
52
|
+
check: "constructor-name"
|
|
53
|
+
};
|
|
54
|
+
for (const candidate of candidates) if (isRecord(candidate) && typeof candidate.displayName === "string" && (isRecord(candidate.actions) || typeof candidate.actions === "function" || typeof candidate.getAction === "function")) return {
|
|
55
|
+
piece: candidate,
|
|
56
|
+
check: "structural"
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async function loadPiece(entryPath) {
|
|
60
|
+
let mod;
|
|
61
|
+
try {
|
|
62
|
+
mod = await import(
|
|
63
|
+
/* @vite-ignore */
|
|
64
|
+
pathToFileURL(entryPath).href
|
|
65
|
+
);
|
|
66
|
+
} catch {
|
|
67
|
+
mod = createRequire(import.meta.url)(entryPath);
|
|
68
|
+
}
|
|
69
|
+
const found = findPiece(mod);
|
|
70
|
+
if (!found) throw new Error(`No Piece export found in ${entryPath}. Export keys: ${Object.keys(mod).join(", ")}`);
|
|
71
|
+
return {
|
|
72
|
+
...found,
|
|
73
|
+
entryPath
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async function loadPieceFromDir(pieceDir) {
|
|
77
|
+
return loadPiece(resolveEntry(pieceDir));
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/pieces/activepieces/descriptor.ts
|
|
81
|
+
function describeHandshake(trigger) {
|
|
82
|
+
const strategy = trigger.handshakeConfiguration?.strategy;
|
|
83
|
+
if (!strategy || strategy === "NONE") return void 0;
|
|
84
|
+
return {
|
|
85
|
+
strategy,
|
|
86
|
+
paramName: trigger.handshakeConfiguration?.paramName
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function hasResolver(prop) {
|
|
90
|
+
return typeof prop.options === "function" || typeof prop.props === "function";
|
|
91
|
+
}
|
|
92
|
+
function toPropDescriptor(name, prop, resolverId) {
|
|
93
|
+
const dynamic = hasResolver(prop);
|
|
94
|
+
const descriptor = {
|
|
95
|
+
name,
|
|
96
|
+
displayName: prop.displayName ?? name,
|
|
97
|
+
type: prop.type ?? "UNKNOWN",
|
|
98
|
+
required: prop.required ?? false,
|
|
99
|
+
hasDynamicResolver: dynamic
|
|
100
|
+
};
|
|
101
|
+
if (typeof prop.description === "string" && prop.description !== "") descriptor.description = prop.description;
|
|
102
|
+
if (typeof prop.placeholder === "string" && prop.placeholder !== "") descriptor.placeholder = prop.placeholder;
|
|
103
|
+
if (prop.defaultValue !== void 0) descriptor.defaultValue = prop.defaultValue;
|
|
104
|
+
if (dynamic && resolverId) descriptor.dynamicResolverId = resolverId;
|
|
105
|
+
if (dynamic && Array.isArray(prop.refreshers)) descriptor.refreshers = prop.refreshers.filter((entry) => typeof entry === "string");
|
|
106
|
+
if (typeof prop.options === "object" && Array.isArray(prop.options.options)) descriptor.staticOptions = prop.options.options.map((o) => ({
|
|
107
|
+
label: o.label,
|
|
108
|
+
value: o.value
|
|
109
|
+
}));
|
|
110
|
+
if (prop.properties && typeof prop.properties === "object") {
|
|
111
|
+
const nested = describeProperties(prop.properties);
|
|
112
|
+
if (nested.length > 0) descriptor.properties = nested;
|
|
113
|
+
}
|
|
114
|
+
return descriptor;
|
|
115
|
+
}
|
|
116
|
+
function describeProperties(props, resolverIdFor) {
|
|
117
|
+
if (!props || typeof props !== "object") return [];
|
|
118
|
+
return Object.entries(props).filter((entry) => isPropertyObject(entry[1])).map(([propName, prop]) => toPropDescriptor(propName, prop, resolverIdFor?.(propName)));
|
|
119
|
+
}
|
|
120
|
+
function isPropertyObject(value) {
|
|
121
|
+
return value !== null && typeof value === "object";
|
|
122
|
+
}
|
|
123
|
+
function buildDescriptor(piece, source) {
|
|
124
|
+
const actions = Object.entries(getActions(piece)).map(([actionName, action]) => ({
|
|
125
|
+
name: action.name ?? actionName,
|
|
126
|
+
displayName: action.displayName ?? actionName,
|
|
127
|
+
description: action.description,
|
|
128
|
+
requireAuth: action.requireAuth ?? false,
|
|
129
|
+
props: describeProperties(action.props, (propName) => `activepieces:${source.packageName}#${actionName}.${propName}`)
|
|
130
|
+
}));
|
|
131
|
+
const triggers = Object.entries(getTriggers(piece)).map(([triggerName, trigger]) => ({
|
|
132
|
+
name: trigger.name ?? triggerName,
|
|
133
|
+
displayName: trigger.displayName ?? triggerName,
|
|
134
|
+
description: trigger.description,
|
|
135
|
+
strategy: trigger.type ?? "UNKNOWN",
|
|
136
|
+
testStrategy: trigger.testStrategy,
|
|
137
|
+
requireAuth: trigger.requireAuth ?? false,
|
|
138
|
+
props: describeProperties(trigger.props, (propName) => `activepieces:${source.packageName}#${triggerName}.${propName}`),
|
|
139
|
+
hasSampleData: trigger.sampleData !== void 0 && trigger.sampleData !== null,
|
|
140
|
+
handshake: describeHandshake(trigger)
|
|
141
|
+
}));
|
|
142
|
+
const descriptor = {
|
|
143
|
+
id: `activepieces:${source.packageName}`,
|
|
144
|
+
source,
|
|
145
|
+
displayName: piece.displayName,
|
|
146
|
+
description: piece.description,
|
|
147
|
+
logoUrl: piece.logoUrl,
|
|
148
|
+
categories: piece.categories,
|
|
149
|
+
minimumSupportedRelease: piece.minimumSupportedRelease,
|
|
150
|
+
maximumSupportedRelease: piece.maximumSupportedRelease,
|
|
151
|
+
actions,
|
|
152
|
+
triggers
|
|
153
|
+
};
|
|
154
|
+
if (piece.auth && typeof piece.auth === "object") {
|
|
155
|
+
const authProps = piece.auth.props && typeof piece.auth.props === "object" ? describeProperties(piece.auth.props) : [];
|
|
156
|
+
descriptor.auth = {
|
|
157
|
+
type: piece.auth.type ?? "UNKNOWN",
|
|
158
|
+
displayName: piece.auth.displayName,
|
|
159
|
+
description: piece.auth.description,
|
|
160
|
+
required: piece.auth.required,
|
|
161
|
+
...authProps.length > 0 ? { props: authProps } : {}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return descriptor;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
export { loadPieceFromDir as i, describeProperties as n, loadPiece as r, buildDescriptor as t };
|
|
168
|
+
|
|
169
|
+
//# sourceMappingURL=descriptor-DXbWuxhE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor-DXbWuxhE.js","names":[],"sources":["../src/pieces/activepieces/loader.ts","../src/pieces/activepieces/descriptor.ts"],"sourcesContent":["import { readFileSync, realpathSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { ApPiece } from \"./types.js\";\n\nexport interface LoadedPiece {\n piece: ApPiece;\n entryPath: string;\n // Which duck-type check identified the export.\n check: \"constructor-name\" | \"structural\";\n}\n\n// Resolves a bundle's entry file. Published bundles typically carry only\n// `main` (no `exports`, no `type` — they are CJS).\nexport function resolveEntry(pieceDir: string): string {\n const raw = readFileSync(path.join(pieceDir, \"package.json\"), \"utf8\");\n const pkg = JSON.parse(raw) as {\n main?: string;\n module?: string;\n exports?: Record<string, unknown>;\n };\n const dotExport = pkg.exports?.[\".\"];\n const candidates: (string | undefined)[] = [];\n if (typeof dotExport === \"string\") candidates.push(dotExport);\n if (dotExport && typeof dotExport === \"object\") {\n const cond = dotExport as Record<string, unknown>;\n for (const key of [\"import\", \"require\", \"default\"]) {\n const value = cond[key];\n if (typeof value === \"string\") candidates.push(value);\n }\n }\n candidates.push(pkg.main, pkg.module, \"src/index.js\", \"index.js\", \"main.js\");\n // A bundle's manifest (or a symlink inside it) must not point the entry\n // outside pieceDir; realpath so a symlink can't launder the escape.\n const root = realpathSync(pieceDir);\n const rootWithSep = root + path.sep;\n for (const candidate of candidates) {\n if (!candidate) continue;\n const abs = path.resolve(pieceDir, candidate);\n let real: string;\n try {\n real = realpathSync(abs);\n } catch {\n continue; // candidate doesn't exist; try the next one\n }\n if (real !== root && !real.startsWith(rootWithSep)) continue;\n return abs;\n }\n throw new Error(`No entry file found for piece bundle at ${pieceDir}`);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n// Identify the piece by constructor name (their own loader's check), falling\n// back to structure for bundles minified without keepNames. No `instanceof`.\nfunction findPiece(\n mod: Record<string, unknown>,\n): Pick<LoadedPiece, \"piece\" | \"check\"> | undefined {\n const candidates: unknown[] = [\n ...Object.values(mod),\n ...(isRecord(mod.default) ? Object.values(mod.default) : []),\n mod.default,\n ];\n for (const candidate of candidates) {\n if (isRecord(candidate) && candidate.constructor.name === \"Piece\") {\n return {\n piece: candidate as unknown as ApPiece,\n check: \"constructor-name\",\n };\n }\n }\n for (const candidate of candidates) {\n if (\n isRecord(candidate) &&\n typeof candidate.displayName === \"string\" &&\n (isRecord(candidate.actions) ||\n typeof candidate.actions === \"function\" ||\n typeof candidate.getAction === \"function\")\n ) {\n return { piece: candidate as unknown as ApPiece, check: \"structural\" };\n }\n }\n return undefined;\n}\n\n// ESM-first `import()` — Node's CJS interop handles the typical CJS bundle —\n// with a `require` fallback.\nexport async function loadPiece(entryPath: string): Promise<LoadedPiece> {\n let mod: Record<string, unknown>;\n try {\n mod = (await import(\n /* @vite-ignore */ pathToFileURL(entryPath).href\n )) as Record<string, unknown>;\n } catch {\n const require = createRequire(import.meta.url);\n mod = require(entryPath) as Record<string, unknown>;\n }\n const found = findPiece(mod);\n if (!found) {\n throw new Error(\n `No Piece export found in ${entryPath}. Export keys: ${Object.keys(mod).join(\", \")}`,\n );\n }\n return { ...found, entryPath };\n}\n\nexport async function loadPieceFromDir(pieceDir: string): Promise<LoadedPiece> {\n return loadPiece(resolveEntry(pieceDir));\n}\n","import {\n getActions,\n getTriggers,\n type ApPiece,\n type ApProperty,\n type ApPropertyType,\n type ApDropdownOption,\n type ApTrigger,\n type ApTriggerStrategy,\n} from \"./types.js\";\n\nexport interface PiecePropDescriptor {\n name: string;\n displayName: string;\n description?: string;\n placeholder?: string;\n type: ApPropertyType;\n required: boolean;\n defaultValue?: unknown;\n // STATIC_DROPDOWN choices, extracted so the editor needs no runtime call.\n staticOptions?: ApDropdownOption[];\n // True when the prop carries a design-time resolver (DROPDOWN options() / DYNAMIC props()).\n hasDynamicResolver: boolean;\n // Synthesised domain-provider id: activepieces:<pkg>#<action>.<prop> (doc 08 §6.3).\n // Only top-level props get one; nested resolvers are not addressable yet.\n dynamicResolverId?: string;\n // Sibling prop names whose values feed the resolver; the editor re-runs\n // it when any of them changes.\n refreshers?: string[];\n // Nested shape: an ARRAY item's fields, or the props a DYNAMIC resolver\n // produced (see describeProperties). OBJECT props are free-form and carry none.\n properties?: PiecePropDescriptor[];\n}\n\nexport interface PieceActionDescriptor {\n name: string;\n displayName: string;\n description?: string;\n // UI metadata only — not a credential contract (spike finding).\n requireAuth: boolean;\n props: PiecePropDescriptor[];\n}\n\nexport interface PieceTriggerDescriptor {\n name: string;\n displayName: string;\n description?: string;\n strategy: ApTriggerStrategy;\n testStrategy?: string;\n requireAuth: boolean;\n props: PiecePropDescriptor[];\n hasSampleData: boolean;\n // How the sender proves the endpoint exists before it will register it.\n // Absent when the trigger declares no handshake, or declares NONE.\n handshake?: { strategy: string; paramName?: string };\n}\n\nexport interface PieceAuthDescriptor {\n type: ApPropertyType;\n displayName?: string;\n description?: string;\n required?: boolean;\n // CUSTOM_AUTH's own fields. Without them a connection form has nothing to\n // ask for, which is what a piece read from a package rather than a published\n // listing would otherwise leave the editor with.\n props?: PiecePropDescriptor[];\n}\n\n// NONE is how the framework spells \"no handshake\", so it is not carried:\n// a caller checking the field would otherwise have to know that too.\nfunction describeHandshake(\n trigger: ApTrigger,\n): { strategy: string; paramName?: string } | undefined {\n const strategy = trigger.handshakeConfiguration?.strategy;\n if (!strategy || strategy === \"NONE\") return undefined;\n return { strategy, paramName: trigger.handshakeConfiguration?.paramName };\n}\n\nexport interface PieceSource {\n packageName: string;\n version: string;\n}\n\n// Serializable descriptor of an adapted piece; the engine never learns\n// Activepieces exists (doc 08 §6.2).\nexport interface PieceDescriptor {\n id: string;\n source: PieceSource;\n displayName: string;\n description?: string;\n logoUrl?: string;\n categories?: string[];\n auth?: PieceAuthDescriptor;\n minimumSupportedRelease?: string;\n maximumSupportedRelease?: string;\n actions: PieceActionDescriptor[];\n triggers: PieceTriggerDescriptor[];\n}\n\nfunction hasResolver(prop: ApProperty): boolean {\n return typeof prop.options === \"function\" || typeof prop.props === \"function\";\n}\n\nfunction toPropDescriptor(\n name: string,\n prop: ApProperty,\n resolverId?: string,\n): PiecePropDescriptor {\n const dynamic = hasResolver(prop);\n const descriptor: PiecePropDescriptor = {\n name,\n displayName: prop.displayName ?? name,\n type: prop.type ?? \"UNKNOWN\",\n required: prop.required ?? false,\n hasDynamicResolver: dynamic,\n };\n if (typeof prop.description === \"string\" && prop.description !== \"\") {\n descriptor.description = prop.description;\n }\n if (typeof prop.placeholder === \"string\" && prop.placeholder !== \"\") {\n descriptor.placeholder = prop.placeholder;\n }\n if (prop.defaultValue !== undefined) {\n descriptor.defaultValue = prop.defaultValue;\n }\n if (dynamic && resolverId) {\n descriptor.dynamicResolverId = resolverId;\n }\n if (dynamic && Array.isArray(prop.refreshers)) {\n descriptor.refreshers = prop.refreshers.filter(\n (entry): entry is string => typeof entry === \"string\",\n );\n }\n if (typeof prop.options === \"object\" && Array.isArray(prop.options.options)) {\n descriptor.staticOptions = prop.options.options.map((o) => ({\n label: o.label,\n value: o.value,\n }));\n }\n if (prop.properties && typeof prop.properties === \"object\") {\n const nested = describeProperties(prop.properties);\n if (nested.length > 0) descriptor.properties = nested;\n }\n return descriptor;\n}\n\n// Descriptor list for a props map: nested ARRAY items and what a DYNAMIC\n// resolver returns, so the editor never sees raw piece properties.\nexport function describeProperties(\n props: Record<string, ApProperty> | null | undefined,\n resolverIdFor?: (propName: string) => string,\n): PiecePropDescriptor[] {\n if (!props || typeof props !== \"object\") return [];\n return Object.entries(props)\n .filter((entry): entry is [string, ApProperty] =>\n isPropertyObject(entry[1]),\n )\n .map(([propName, prop]) =>\n toPropDescriptor(propName, prop, resolverIdFor?.(propName)),\n );\n}\n\n// Bundles are duck-typed; a props map may carry non-object junk.\nfunction isPropertyObject(value: unknown): value is ApProperty {\n return value !== null && typeof value === \"object\";\n}\n\n// Pure translation over a loaded piece; performs no I/O and never executes\n// piece code beyond the actions()/triggers() accessors.\nexport function buildDescriptor(\n piece: ApPiece,\n source: PieceSource,\n): PieceDescriptor {\n const actions = Object.entries(getActions(piece)).map(\n ([actionName, action]): PieceActionDescriptor => ({\n name: action.name ?? actionName,\n displayName: action.displayName ?? actionName,\n description: action.description,\n requireAuth: action.requireAuth ?? false,\n props: describeProperties(\n action.props,\n (propName) =>\n `activepieces:${source.packageName}#${actionName}.${propName}`,\n ),\n }),\n );\n\n const triggers = Object.entries(getTriggers(piece)).map(\n ([triggerName, trigger]): PieceTriggerDescriptor => ({\n name: trigger.name ?? triggerName,\n displayName: trigger.displayName ?? triggerName,\n description: trigger.description,\n strategy: trigger.type ?? \"UNKNOWN\",\n testStrategy: trigger.testStrategy,\n requireAuth: trigger.requireAuth ?? false,\n props: describeProperties(\n trigger.props,\n (propName) =>\n `activepieces:${source.packageName}#${triggerName}.${propName}`,\n ),\n hasSampleData:\n trigger.sampleData !== undefined && trigger.sampleData !== null,\n handshake: describeHandshake(trigger),\n }),\n );\n\n const descriptor: PieceDescriptor = {\n id: `activepieces:${source.packageName}`,\n source,\n displayName: piece.displayName,\n description: piece.description,\n logoUrl: piece.logoUrl,\n categories: piece.categories,\n minimumSupportedRelease: piece.minimumSupportedRelease,\n maximumSupportedRelease: piece.maximumSupportedRelease,\n actions,\n triggers,\n };\n if (piece.auth && typeof piece.auth === \"object\") {\n // CUSTOM_AUTH carries a record here; a DYNAMIC prop would carry a\n // resolver function, which is not an auth shape at all.\n const authProps =\n piece.auth.props && typeof piece.auth.props === \"object\"\n ? describeProperties(piece.auth.props)\n : [];\n descriptor.auth = {\n type: piece.auth.type ?? \"UNKNOWN\",\n displayName: piece.auth.displayName,\n description: piece.auth.description,\n required: piece.auth.required,\n ...(authProps.length > 0 ? { props: authProps } : {}),\n };\n }\n return descriptor;\n}\n"],"mappings":"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"}
|