viasocket-apps 0.1.1 → 0.1.2
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 +60 -18
- package/dist/cjs/client.js +10 -4
- package/dist/cjs/http.js +7 -5
- package/dist/cjs/index.js +2 -2
- package/dist/esm/client.d.ts +4 -3
- package/dist/esm/client.js +10 -4
- package/dist/esm/http.d.ts +5 -4
- package/dist/esm/http.js +7 -5
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/types.d.ts +10 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,28 @@ The same calls are documented as plain HTTP in your viaSocket dashboard under
|
|
|
15
15
|
**Integrations → your Apps API integration → API reference**. Use whichever suits your stack;
|
|
16
16
|
this package is a thin, typed layer over those endpoints and adds nothing they don't have.
|
|
17
17
|
|
|
18
|
+
## For coding agents
|
|
19
|
+
|
|
20
|
+
[](https://skills.sh/Walkover-Web-Solution/viasocket-apps)
|
|
21
|
+
|
|
22
|
+
This repo also carries the **viaSocket integrations skill** — what a coding agent reads to
|
|
23
|
+
integrate any app into your product, with or without this package. Install it into Claude Code,
|
|
24
|
+
Cursor, Codex, Copilot, Gemini CLI, Windsurf, Cline and 20+ other agents:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npx skills add Walkover-Web-Solution/viasocket-apps
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Add `-g` to install it for every project. No install? Paste this into your agent:
|
|
31
|
+
`Read https://flow.viasocket.com/documentation/skill.md and follow it.`
|
|
32
|
+
|
|
33
|
+
The agent needs three values from the viaSocket dashboard (**Integrations → Create embed →
|
|
34
|
+
Install Code**): `org_id` and `project_id`, which it asks for, and `VIASOCKET_EMBED_SECRET`, which
|
|
35
|
+
goes in your server's `.env`. The skill is
|
|
36
|
+
[skills/viasocket-integrations/SKILL.md](skills/viasocket-integrations/SKILL.md), generated by
|
|
37
|
+
viaSocket and mirrored here; the same text filled with your ids is served at
|
|
38
|
+
`https://flow.viasocket.com/documentation/skill.md?org=<org_id>&project=<project_id>`.
|
|
39
|
+
|
|
18
40
|
## Server
|
|
19
41
|
|
|
20
42
|
```js
|
|
@@ -49,12 +71,14 @@ const result = await viasocket.runAction(scriptId, actionVersionId, {
|
|
|
49
71
|
column_key: true
|
|
50
72
|
})
|
|
51
73
|
|
|
52
|
-
// 5. Or subscribe to an event. Needs only the connection, not enable().
|
|
74
|
+
// 5. Or subscribe to an event. Needs only the connection, not enable(). `code` is a handler we
|
|
75
|
+
// run on our servers when the event fires — see "Events" below. Save the scriptId it returns
|
|
76
|
+
// together with the inputData and handler you sent: the flows list never returns those.
|
|
53
77
|
const subscription = await user.subscribe(triggerVersionId, {
|
|
54
78
|
authId,
|
|
55
79
|
inputData: { channel_id: ['C08SXCV3J85'] },
|
|
56
|
-
|
|
57
|
-
meta: {
|
|
80
|
+
code: handler,
|
|
81
|
+
meta: { user_id: 'user_123' }
|
|
58
82
|
})
|
|
59
83
|
```
|
|
60
84
|
|
|
@@ -79,27 +103,45 @@ const { authId } = await connect({ embedToken, serviceId: 'rowbu58rc' })
|
|
|
79
103
|
| `user.token()` | Signed embed token for that user; hand it to the frontend. |
|
|
80
104
|
| `user.enable(serviceId, authId)` | Turn a connection into a `scriptId` for running actions. Once per connection. |
|
|
81
105
|
| `user.findEnabled(serviceId)` | The `scriptId` if this app is already enabled, else `null`. Call before `enable`. |
|
|
82
|
-
| `user.listOptions(actionVersionId, { fieldKey, authId, existingFields })` | The values a field accepts, as `{ options, offset }`. Both response shapes normalised. |
|
|
106
|
+
| `user.listOptions(actionVersionId, { fieldKey, authId, existingFields })` | The values a field accepts, as `{ options, offset }`. Both response shapes normalised; an unreadable connection (revoked, another user's `auth_id`) throws a `ViaSocketError` with status 400 even though the API answers 200. |
|
|
83
107
|
| `viasocket.runAction(scriptId, actionVersionId, inputData)` | Run one action. Returns the app's own response. |
|
|
84
|
-
| `user.subscribe(triggerVersionId, { authId, inputData,
|
|
108
|
+
| `user.subscribe(triggerVersionId, { authId, inputData, code \| webhook, meta })` | Subscribe to an app event with a handler we run per event. Returns `{ scriptId, hookUrl, … }`. |
|
|
85
109
|
| `user.updateSubscription(scriptId, { code, meta })` | Change a live subscription in place. |
|
|
86
110
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
## Events
|
|
112
|
+
|
|
113
|
+
Subscribing to an event means telling us **what to do when it fires**. That is `code`: a handler we
|
|
114
|
+
run on viaSocket's servers per event, with the event in `context.req.body`. Your server is not in
|
|
115
|
+
the path. Most often the handler runs an action in another app the user connected — the run URL
|
|
116
|
+
takes no token, the `script_id` is the credential — or calls your own API with your own auth header
|
|
117
|
+
baked in. It is a string executed away from your process, so it must stand alone: no imports,
|
|
118
|
+
nothing from your codebase; `axios`, `fetch` and `context` are in scope.
|
|
91
119
|
|
|
92
120
|
```js
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
121
|
+
// A string, not a function: it runs on viaSocket's servers each time the event fires.
|
|
122
|
+
// In scope there: axios, fetch, context. The event the app sent is context.req.body.
|
|
123
|
+
// ${…} are YOUR variables, baked in per user when you subscribe.
|
|
124
|
+
const handler = `
|
|
125
|
+
const event = context.req.body
|
|
126
|
+
const response = await fetch("https://flow.sokt.io/func/${slackScriptId}", {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: { "Content-Type": "application/json" },
|
|
129
|
+
body: JSON.stringify({
|
|
130
|
+
action_version_id: "${slackSendMessageVersionId}",
|
|
131
|
+
inputData: { channel_id: "${channelPickedAtSetup}", text: "New mail from " + event.from + ": " + event.subject }
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
return await response.json()
|
|
135
|
+
`
|
|
136
|
+
|
|
137
|
+
await user.subscribe(gmailNewMailVersionId, { authId, inputData: {}, code: handler, meta: { user_id: uniqueIdentifier } })
|
|
102
138
|
```
|
|
139
|
+
|
|
140
|
+
`webhook` is the edge case: a public, unauthenticated URL of yours that we POST every raw event to.
|
|
141
|
+
Use it only when that is what you want, and never both.
|
|
142
|
+
|
|
143
|
+
Some triggers are polled rather than pushed. For those, `inputData` may carry `scheduledTime`, the
|
|
144
|
+
minutes between checks, as a string: `"5"` or `"15"`.
|
|
103
145
|
| `user.listFlows()` | Every enabled app and subscription this user has. |
|
|
104
146
|
| `user.disableFlow(scriptId)` / `user.enableFlow(scriptId)` | Turn a flow off or back on. Disabling a subscription ends it. |
|
|
105
147
|
| `user.listConnections()` | Every app this user has connected. |
|
package/dist/cjs/client.js
CHANGED
|
@@ -124,16 +124,22 @@ class UserScope {
|
|
|
124
124
|
});
|
|
125
125
|
if (Array.isArray(data))
|
|
126
126
|
return { options: data, offset: null };
|
|
127
|
+
// A connection that cannot be read (revoked, or another user's auth_id) is answered with 200 and
|
|
128
|
+
// success: true, the failure nested in data. It is an error, and the message is the explanation.
|
|
129
|
+
if (data?.response && Number(data.response.status) >= 400) {
|
|
130
|
+
throw new errors_js_1.ViaSocketError(data.response.data?.message || 'list-options failed', { status: Number(data.response.status), body: data });
|
|
131
|
+
}
|
|
127
132
|
return { options: Array.isArray(data?.data) ? data.data : [], offset: data?.offset ?? null };
|
|
128
133
|
}
|
|
129
134
|
/**
|
|
130
|
-
* Subscribes to an app event
|
|
131
|
-
*
|
|
132
|
-
*
|
|
135
|
+
* Subscribes to an app event with a handler: `code`, a self-contained script we run on our
|
|
136
|
+
* servers each time the event fires, with the event at `context.req.body`. It runs an action in
|
|
137
|
+
* another app the user connected, or calls your own API. `webhook` (a public URL we POST raw
|
|
138
|
+
* events to) is the edge case. Exactly one of the two.
|
|
133
139
|
*/
|
|
134
140
|
async subscribe(triggerVersionId, params) {
|
|
135
141
|
if (!params?.webhook === !params?.code) {
|
|
136
|
-
throw new errors_js_1.ViaSocketError('subscribe: pass exactly one of `
|
|
142
|
+
throw new errors_js_1.ViaSocketError('subscribe: pass exactly one of `code` (a handler we run per event) or `webhook`', { status: null });
|
|
137
143
|
}
|
|
138
144
|
const body = {
|
|
139
145
|
auth_id: required('authId', params.authId),
|
package/dist/cjs/http.js
CHANGED
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
* that message is almost always the explanation.
|
|
9
9
|
*
|
|
10
10
|
* The action runner is the exception: some actions answer in that envelope, many hand back their
|
|
11
|
-
* own result at the top level with no `success` or `data` key at all (Gmail's list-mails returns
|
|
12
|
-
* `{ emails, nextPageToken }`)
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* own result at the top level — with no `success` or `data` key at all (Gmail's list-mails returns
|
|
12
|
+
* `{ emails, nextPageToken }`), or with a `success` key of their own and no `data` (Slack's "Find
|
|
13
|
+
* Public channel" returns `{ success, channels }`). So a body is the envelope only when it carries a
|
|
14
|
+
* boolean `success` AND a `data` key, or is a refusal (`success: false`); anything else is the
|
|
15
|
+
* result itself. Getting this wrong returned `undefined` for every such action.
|
|
15
16
|
*/
|
|
16
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
18
|
exports.request = request;
|
|
@@ -35,7 +36,8 @@ async function request(options) {
|
|
|
35
36
|
catch {
|
|
36
37
|
payload = text;
|
|
37
38
|
}
|
|
38
|
-
const
|
|
39
|
+
const shaped = payload !== null && typeof payload === 'object' ? payload : null;
|
|
40
|
+
const isEnvelope = shaped !== null && typeof shaped.success === 'boolean' && ('data' in shaped || shaped.success === false);
|
|
39
41
|
const envelope = isEnvelope ? payload : null;
|
|
40
42
|
if (!response.ok || envelope?.success === false) {
|
|
41
43
|
const raw = payload && typeof payload === 'object' ? payload : null;
|
package/dist/cjs/index.js
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* import { ViaSocket } from 'viasocket-apps'
|
|
6
6
|
*
|
|
7
7
|
* const viasocket = new ViaSocket({ orgId, projectId, secret: process.env.VIASOCKET_EMBED_SECRET })
|
|
8
|
-
* const user = viasocket.user(
|
|
8
|
+
* const user = viasocket.user(uniqueIdentifier)
|
|
9
9
|
*
|
|
10
10
|
* const token = await user.token() // for the connect popup
|
|
11
11
|
* const { scriptId } = await user.enable(serviceId, authId) // once, for actions
|
|
12
12
|
* const { options } = await user.listOptions(actionVersionId, {...}) // fill a dropdown
|
|
13
13
|
* const result = await viasocket.runAction(scriptId, actionVersionId, inputData)
|
|
14
|
-
* const sub = await user.subscribe(triggerVersionId, { authId, inputData,
|
|
14
|
+
* const sub = await user.subscribe(triggerVersionId, { authId, inputData, code: handler })
|
|
15
15
|
*
|
|
16
16
|
* The browser half — opening the connect popup — lives at 'viasocket-apps/browser'.
|
|
17
17
|
*/
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -56,9 +56,10 @@ export declare class UserScope {
|
|
|
56
56
|
*/
|
|
57
57
|
listOptions(actionVersionId: string, params: ListOptionsParams): Promise<ListOptionsResult>;
|
|
58
58
|
/**
|
|
59
|
-
* Subscribes to an app event
|
|
60
|
-
*
|
|
61
|
-
*
|
|
59
|
+
* Subscribes to an app event with a handler: `code`, a self-contained script we run on our
|
|
60
|
+
* servers each time the event fires, with the event at `context.req.body`. It runs an action in
|
|
61
|
+
* another app the user connected, or calls your own API. `webhook` (a public URL we POST raw
|
|
62
|
+
* events to) is the edge case. Exactly one of the two.
|
|
62
63
|
*/
|
|
63
64
|
subscribe(triggerVersionId: string, params: SubscribeParams): Promise<SubscribeResult>;
|
|
64
65
|
/** Changes a live subscription in place. The script_id does not change. */
|
package/dist/esm/client.js
CHANGED
|
@@ -120,16 +120,22 @@ export class UserScope {
|
|
|
120
120
|
});
|
|
121
121
|
if (Array.isArray(data))
|
|
122
122
|
return { options: data, offset: null };
|
|
123
|
+
// A connection that cannot be read (revoked, or another user's auth_id) is answered with 200 and
|
|
124
|
+
// success: true, the failure nested in data. It is an error, and the message is the explanation.
|
|
125
|
+
if (data?.response && Number(data.response.status) >= 400) {
|
|
126
|
+
throw new ViaSocketError(data.response.data?.message || 'list-options failed', { status: Number(data.response.status), body: data });
|
|
127
|
+
}
|
|
123
128
|
return { options: Array.isArray(data?.data) ? data.data : [], offset: data?.offset ?? null };
|
|
124
129
|
}
|
|
125
130
|
/**
|
|
126
|
-
* Subscribes to an app event
|
|
127
|
-
*
|
|
128
|
-
*
|
|
131
|
+
* Subscribes to an app event with a handler: `code`, a self-contained script we run on our
|
|
132
|
+
* servers each time the event fires, with the event at `context.req.body`. It runs an action in
|
|
133
|
+
* another app the user connected, or calls your own API. `webhook` (a public URL we POST raw
|
|
134
|
+
* events to) is the edge case. Exactly one of the two.
|
|
129
135
|
*/
|
|
130
136
|
async subscribe(triggerVersionId, params) {
|
|
131
137
|
if (!params?.webhook === !params?.code) {
|
|
132
|
-
throw new ViaSocketError('subscribe: pass exactly one of `
|
|
138
|
+
throw new ViaSocketError('subscribe: pass exactly one of `code` (a handler we run per event) or `webhook`', { status: null });
|
|
133
139
|
}
|
|
134
140
|
const body = {
|
|
135
141
|
auth_id: required('authId', params.authId),
|
package/dist/esm/http.d.ts
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* that message is almost always the explanation.
|
|
8
8
|
*
|
|
9
9
|
* The action runner is the exception: some actions answer in that envelope, many hand back their
|
|
10
|
-
* own result at the top level with no `success` or `data` key at all (Gmail's list-mails returns
|
|
11
|
-
* `{ emails, nextPageToken }`)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* own result at the top level — with no `success` or `data` key at all (Gmail's list-mails returns
|
|
11
|
+
* `{ emails, nextPageToken }`), or with a `success` key of their own and no `data` (Slack's "Find
|
|
12
|
+
* Public channel" returns `{ success, channels }`). So a body is the envelope only when it carries a
|
|
13
|
+
* boolean `success` AND a `data` key, or is a refusal (`success: false`); anything else is the
|
|
14
|
+
* result itself. Getting this wrong returned `undefined` for every such action.
|
|
14
15
|
*/
|
|
15
16
|
import type { Envelope } from './types.js';
|
|
16
17
|
export interface RequestOptions {
|
package/dist/esm/http.js
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* that message is almost always the explanation.
|
|
8
8
|
*
|
|
9
9
|
* The action runner is the exception: some actions answer in that envelope, many hand back their
|
|
10
|
-
* own result at the top level with no `success` or `data` key at all (Gmail's list-mails returns
|
|
11
|
-
* `{ emails, nextPageToken }`)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* own result at the top level — with no `success` or `data` key at all (Gmail's list-mails returns
|
|
11
|
+
* `{ emails, nextPageToken }`), or with a `success` key of their own and no `data` (Slack's "Find
|
|
12
|
+
* Public channel" returns `{ success, channels }`). So a body is the envelope only when it carries a
|
|
13
|
+
* boolean `success` AND a `data` key, or is a refusal (`success: false`); anything else is the
|
|
14
|
+
* result itself. Getting this wrong returned `undefined` for every such action.
|
|
14
15
|
*/
|
|
15
16
|
import { ViaSocketError } from './errors.js';
|
|
16
17
|
export async function request(options) {
|
|
@@ -32,7 +33,8 @@ export async function request(options) {
|
|
|
32
33
|
catch {
|
|
33
34
|
payload = text;
|
|
34
35
|
}
|
|
35
|
-
const
|
|
36
|
+
const shaped = payload !== null && typeof payload === 'object' ? payload : null;
|
|
37
|
+
const isEnvelope = shaped !== null && typeof shaped.success === 'boolean' && ('data' in shaped || shaped.success === false);
|
|
36
38
|
const envelope = isEnvelope ? payload : null;
|
|
37
39
|
if (!response.ok || envelope?.success === false) {
|
|
38
40
|
const raw = payload && typeof payload === 'object' ? payload : null;
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* import { ViaSocket } from 'viasocket-apps'
|
|
5
5
|
*
|
|
6
6
|
* const viasocket = new ViaSocket({ orgId, projectId, secret: process.env.VIASOCKET_EMBED_SECRET })
|
|
7
|
-
* const user = viasocket.user(
|
|
7
|
+
* const user = viasocket.user(uniqueIdentifier)
|
|
8
8
|
*
|
|
9
9
|
* const token = await user.token() // for the connect popup
|
|
10
10
|
* const { scriptId } = await user.enable(serviceId, authId) // once, for actions
|
|
11
11
|
* const { options } = await user.listOptions(actionVersionId, {...}) // fill a dropdown
|
|
12
12
|
* const result = await viasocket.runAction(scriptId, actionVersionId, inputData)
|
|
13
|
-
* const sub = await user.subscribe(triggerVersionId, { authId, inputData,
|
|
13
|
+
* const sub = await user.subscribe(triggerVersionId, { authId, inputData, code: handler })
|
|
14
14
|
*
|
|
15
15
|
* The browser half — opening the connect popup — lives at 'viasocket-apps/browser'.
|
|
16
16
|
*/
|
package/dist/esm/index.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* import { ViaSocket } from 'viasocket-apps'
|
|
5
5
|
*
|
|
6
6
|
* const viasocket = new ViaSocket({ orgId, projectId, secret: process.env.VIASOCKET_EMBED_SECRET })
|
|
7
|
-
* const user = viasocket.user(
|
|
7
|
+
* const user = viasocket.user(uniqueIdentifier)
|
|
8
8
|
*
|
|
9
9
|
* const token = await user.token() // for the connect popup
|
|
10
10
|
* const { scriptId } = await user.enable(serviceId, authId) // once, for actions
|
|
11
11
|
* const { options } = await user.listOptions(actionVersionId, {...}) // fill a dropdown
|
|
12
12
|
* const result = await viasocket.runAction(scriptId, actionVersionId, inputData)
|
|
13
|
-
* const sub = await user.subscribe(triggerVersionId, { authId, inputData,
|
|
13
|
+
* const sub = await user.subscribe(triggerVersionId, { authId, inputData, code: handler })
|
|
14
14
|
*
|
|
15
15
|
* The browser half — opening the connect popup — lives at 'viasocket-apps/browser'.
|
|
16
16
|
*/
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -50,19 +50,23 @@ export interface EnableResult {
|
|
|
50
50
|
}
|
|
51
51
|
export interface SubscribeParams {
|
|
52
52
|
authId: string;
|
|
53
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* The event's own configuration — which channel, which sheet. For polled triggers it may also
|
|
55
|
+
* carry `scheduledTime`, the minutes between checks as a string: "5" or "15".
|
|
56
|
+
*/
|
|
54
57
|
inputData: Record<string, unknown>;
|
|
55
|
-
/** Your endpoint. We POST every event to it. Use this or `code`, not both. */
|
|
56
|
-
webhook?: string;
|
|
57
58
|
/**
|
|
58
|
-
*
|
|
59
|
+
* What to do when the event fires: a script we run on viaSocket's servers per event. The
|
|
60
|
+
* default. It runs an action in another app the user connected, or calls your own API.
|
|
59
61
|
*
|
|
60
62
|
* It is a string of JavaScript executed away from your process, so it must stand alone — no
|
|
61
63
|
* imports and no reference to anything in your codebase. In scope there: `axios`, `fetch` and
|
|
62
|
-
* `context`; the event the app sent is `context.req.body`. Bake in any value of yours (a
|
|
63
|
-
*
|
|
64
|
+
* `context`; the event the app sent is `context.req.body`. Bake in any value of yours (a
|
|
65
|
+
* script_id, a picked id, an API key) when you build the string. Exactly one of `code` or `webhook`.
|
|
64
66
|
*/
|
|
65
67
|
code?: string;
|
|
68
|
+
/** Edge case: a public, unauthenticated URL of yours. We POST every raw event to it. */
|
|
69
|
+
webhook?: string;
|
|
66
70
|
/** Anything of yours; handed back with every delivery. */
|
|
67
71
|
meta?: Record<string, unknown>;
|
|
68
72
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viasocket-apps",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Server-side client for the viaSocket Apps API: let your users connect Slack, GitHub, Google Sheets or any of 2,300+ apps from your own product, then run actions and receive events through one API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"viasocket",
|