@visualautomate/plugin-sdk 0.7.0 → 0.8.0

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/README.md CHANGED
@@ -41,13 +41,12 @@ in your manifest. `data` is what the next steps read.
41
41
  | `storage` | the account's files — `put`, `get`, `delete`, `list`, `head`, `signedUrl`, `shareUrl`, `download` |
42
42
  | `state` | values that outlive one run, scoped to the workflow — `get`, `set` (at most 128 KB, `ttlSeconds` at least 60), `delete`, `list` (`{ keys: [{ key, expiresAt }], complete }`) |
43
43
  | `media` | ffmpeg, by operation name. Absent when the server has no sandbox configured |
44
- | `connections` | the accounts this step was configured with. Absent when it declares none, or none was chosen |
45
44
  | `workflowId`, `nodeId`, `userId`, `executionId`, `startedAt`, `secrets` | about the run |
46
45
 
47
- You never see a credential and you never name a host. `connections.get("account")`
48
- takes the name of one of *your* properties, and the platform attaches the token
49
- on the way past — which is what stops a plugin pointing somebody's account
50
- somewhere it chose.
46
+ There is no connected account for your steps: only VisualAutomate's own apps act
47
+ as one (`checkManifest` refuses a `connection` property, `optionsFrom`,
48
+ `action.connection`, a `trigger` and `auth.provider`). A step reaches an API with
49
+ the key a user enters in a `secret` property.
51
50
 
52
51
  ## `simulate`
53
52
 
@@ -72,22 +71,9 @@ platform's have — `get` on a missing key is `null`, `put` takes a string or by
72
71
  `list` is prefix-filtered, a state value over 128 KB or a `ttlSeconds` under 60 is
73
72
  refused, and an expired value reads as `null`.
74
73
 
75
- `connections` and `media` are not simulated. Without a stub, `context.connections`
76
- is absent, as on the platform when no account was chosen, and `context.media`
77
- fails with a sentence saying so. Pass a stub to test the code around them:
78
-
79
- ```ts
80
- await simulate(plugin, {
81
- connections: {
82
- async get() {
83
- return { id: "test", async request() { return { ok: true, status: 200, body: {} } } }
84
- },
85
- },
86
- })
87
- ```
88
-
89
- Asserting on what your plugin *asked* the provider for is a better test than any
90
- imitation of the provider would be.
74
+ `media` is not simulated: `context.media` fails with a sentence saying so. Stub
75
+ `fetch` to test the code around an API call; asserting on what your plugin
76
+ *asked* for is a better test than any imitation of the API would be.
91
77
 
92
78
  ## The platform's code checks
93
79
 
@@ -143,8 +129,9 @@ check.errors // why it would be refused
143
129
  check.warnings // what it would drop or regret
144
130
  ```
145
131
 
146
- The SDK's own reading of the rules the portal enforces, for code steps,
147
- declared steps (`action`), triggers and `optionsFrom`; the portal's word is final.
132
+ The SDK's own reading of the rules the portal enforces, for code steps and
133
+ declared steps (`action`), and the refusal of anything that would act as a
134
+ connected account (`accountUseIn` lists each way in); the portal's word is final.
148
135
 
149
136
  ## The platform's facts
150
137
 
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ACCOUNTS_ARE_OURS: () => ACCOUNTS_ARE_OURS,
33
34
  ALLOWED_PACKAGES: () => ALLOWED_PACKAGES,
34
35
  ALLOWED_PACKAGE_NAMES: () => ALLOWED_PACKAGE_NAMES,
35
36
  BLOCKED_GLOBALS: () => BLOCKED_GLOBALS,
@@ -45,6 +46,7 @@ __export(index_exports, {
45
46
  SANDBOX_TIERS: () => SANDBOX_TIERS,
46
47
  SIGNED_URL_SECONDS: () => SIGNED_URL_SECONDS,
47
48
  STATE_LIMITS: () => STATE_LIMITS,
49
+ accountUseIn: () => accountUseIn,
48
50
  checkManifest: () => checkManifest,
49
51
  createLocalState: () => createLocalState,
50
52
  createLocalStorage: () => createLocalStorage,
@@ -1679,6 +1681,23 @@ var MAX_PAGES = 50;
1679
1681
  var MAX_MEMORY_MB = 128;
1680
1682
  var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1681
1683
  var isRelativePath = (path) => typeof path === "string" && path.trim() !== "" && !/^[a-z][a-z0-9+.-]*:/i.test(path) && !path.startsWith("//");
1684
+ var ACCOUNTS_ARE_OURS = `Only VisualAutomate's own apps act as a connected account: call the API at its own address instead (action.baseUrl, or fetch in code), with a key the user enters in a property of type "secret"`;
1685
+ function accountUseIn(manifest) {
1686
+ if (!isObject(manifest)) return [];
1687
+ const m = manifest;
1688
+ const uses = [];
1689
+ if (isObject(m.properties)) {
1690
+ for (const [key, value] of Object.entries(m.properties)) {
1691
+ if (!isObject(value)) continue;
1692
+ if (value.type === "connection") uses.push(`properties.${key} is a connection: an account to act as`);
1693
+ if (value.optionsFrom !== void 0) uses.push(`properties.${key}.optionsFrom reads its options as an account`);
1694
+ }
1695
+ }
1696
+ if (isObject(m.action) && m.action.connection !== void 0) uses.push("action.connection makes the call as an account");
1697
+ if (m.trigger !== void 0) uses.push("trigger watches an account");
1698
+ if (isObject(m.auth) && typeof m.auth.provider === "string" && m.auth.provider) uses.push(`auth.provider names "${m.auth.provider}"`);
1699
+ return uses;
1700
+ }
1682
1701
  function checkManifest(raw, hasCode) {
1683
1702
  const errors = [];
1684
1703
  const warnings = [];
@@ -1720,6 +1739,8 @@ function checkManifest(raw, hasCode) {
1720
1739
  else {
1721
1740
  if (typeof m.auth.type !== "string" || !AUTH_TYPES.has(m.auth.type)) {
1722
1741
  errors.push(`auth.type must be one of ${[...AUTH_TYPES].join(", ")}.`);
1742
+ } else if (m.auth.type !== "none" && !m.auth.provider) {
1743
+ errors.push(`auth.type "${m.auth.type}" needs a provider to sign in to, and only VisualAutomate's own apps name one: use { "type": "none" } and take the user's key in a property of type "secret".`);
1723
1744
  }
1724
1745
  for (const key of HOST_KEYS) {
1725
1746
  if (key in m.auth) errors.push(`auth.${key} is refused: a manifest may not name a host. The provider's base URL belongs to the platform.`);
@@ -1753,6 +1774,7 @@ function checkManifest(raw, hasCode) {
1753
1774
  if (kind === "code" && !hasCode) {
1754
1775
  errors.push("There is no index.js and no action or trigger block, so this step has nothing to run.");
1755
1776
  }
1777
+ for (const use of accountUseIn(m)) errors.push(`${use}. ${ACCOUNTS_ARE_OURS}`);
1756
1778
  return { ok: errors.length === 0, kind, errors, warnings };
1757
1779
  }
1758
1780
  function checkConnectionName(where, name, typeOf, errors) {