viasocket-apps 0.1.2 → 0.2.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 +6 -1
- package/dist/cjs/browser.js +9 -1
- package/dist/cjs/client.js +68 -0
- package/dist/esm/browser.d.ts +14 -1
- package/dist/esm/browser.js +9 -1
- package/dist/esm/client.d.ts +21 -1
- package/dist/esm/client.js +68 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/types.d.ts +37 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,7 +88,9 @@ const subscription = await user.subscribe(triggerVersionId, {
|
|
|
88
88
|
import { connect } from 'viasocket-apps/browser'
|
|
89
89
|
|
|
90
90
|
// embedToken comes from your backend (user.token()), signed for the signed-in user.
|
|
91
|
-
|
|
91
|
+
// actions (optional): the rowids of the actions and triggers you use — the popup then asks only
|
|
92
|
+
// for their scopes; skipActionSelection goes straight to the app's consent screen.
|
|
93
|
+
const { authId } = await connect({ embedToken, serviceId: 'rowbu58rc', actions: ['<action rowid>'], skipActionSelection: true })
|
|
92
94
|
// Send authId to your backend, which enables the app and stores the script_id.
|
|
93
95
|
```
|
|
94
96
|
|
|
@@ -105,6 +107,9 @@ const { authId } = await connect({ embedToken, serviceId: 'rowbu58rc' })
|
|
|
105
107
|
| `user.findEnabled(serviceId)` | The `scriptId` if this app is already enabled, else `null`. Call before `enable`. |
|
|
106
108
|
| `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. |
|
|
107
109
|
| `viasocket.runAction(scriptId, actionVersionId, inputData)` | Run one action. Returns the app's own response. |
|
|
110
|
+
| `viasocket.catalog.search(key)` | The best 30 apps for what the user typed, as `{ serviceId, name, iconUrl, description }`. No token. |
|
|
111
|
+
| `viasocket.catalog.list({ limit, offset, category })` | Every app, most used first, 200 a page; an empty page ends it. Drop apps whose `versions()` is empty. No token. |
|
|
112
|
+
| `viasocket.catalog.versions(serviceId)` | Every published action and trigger of one app, as `{ actions, triggers }`, each with its `inputjson` schema and version id. No token. |
|
|
108
113
|
| `user.subscribe(triggerVersionId, { authId, inputData, code \| webhook, meta })` | Subscribe to an app event with a handler we run per event. Returns `{ scriptId, hookUrl, … }`. |
|
|
109
114
|
| `user.updateSubscription(scriptId, { code, meta })` | Change a live subscription in place. |
|
|
110
115
|
|
package/dist/cjs/browser.js
CHANGED
|
@@ -101,6 +101,14 @@ async function connect(options) {
|
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
window.addEventListener('message', onMessage);
|
|
104
|
-
|
|
104
|
+
const scriptOptions = {};
|
|
105
|
+
if (options.actions?.length)
|
|
106
|
+
scriptOptions.filteredActions = options.actions;
|
|
107
|
+
if (options.skipActionSelection)
|
|
108
|
+
scriptOptions.skipActionSelection = true;
|
|
109
|
+
if (Object.keys(scriptOptions).length)
|
|
110
|
+
open(options.embedToken, serviceId, scriptOptions);
|
|
111
|
+
else
|
|
112
|
+
open(options.embedToken, serviceId);
|
|
105
113
|
});
|
|
106
114
|
}
|
package/dist/cjs/client.js
CHANGED
|
@@ -15,6 +15,13 @@ const http_js_1 = require("./http.js");
|
|
|
15
15
|
const token_js_1 = require("./token.js");
|
|
16
16
|
const DEFAULT_API_BASE_URL = 'https://flow-api.viasocket.com';
|
|
17
17
|
const DEFAULT_RUN_BASE_URL = 'https://flow.sokt.io';
|
|
18
|
+
/** The public catalog: no token, the same data in every environment. */
|
|
19
|
+
const CATALOG = {
|
|
20
|
+
search: 'https://flow.sokt.io/func/scri12BSufQM',
|
|
21
|
+
list: 'https://plug-service.viasocket.com/api/v1/plugins/all',
|
|
22
|
+
versions: 'https://flow.sokt.io/func/scriolZue69X'
|
|
23
|
+
};
|
|
24
|
+
const LIST_LIMIT_MAX = 200;
|
|
18
25
|
function required(name, value) {
|
|
19
26
|
if (typeof value !== 'string' || !value.trim())
|
|
20
27
|
throw new errors_js_1.ViaSocketError(`${name} is required`, { status: null });
|
|
@@ -42,6 +49,67 @@ class ViaSocket {
|
|
|
42
49
|
// Bound so a bare `fetch` reference still has the right `this` on runtimes that care.
|
|
43
50
|
this.transport = options.fetch ? transport : transport.bind(globalThis);
|
|
44
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The catalog: which apps exist and what each can do. Public, no token, nothing per user — so
|
|
54
|
+
* it lives on the root client and is safe to call from a browser too.
|
|
55
|
+
*/
|
|
56
|
+
catalog = {
|
|
57
|
+
/** The best 30 apps for what the user typed. A type-ahead: an empty key returns nothing. */
|
|
58
|
+
search: async (key) => {
|
|
59
|
+
const url = `${CATALOG.search}?key=${encodeURIComponent(required('key', key))}`;
|
|
60
|
+
const { data } = await (0, http_js_1.request)({ method: 'GET', url, fetchImpl: this.transport });
|
|
61
|
+
return (Array.isArray(data) ? data : []).map((row) => ({
|
|
62
|
+
serviceId: row.service_id,
|
|
63
|
+
name: row.name,
|
|
64
|
+
description: row.description ?? '',
|
|
65
|
+
iconUrl: row.iconurl ?? ''
|
|
66
|
+
}));
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* Every app, most used first, a page at a time (200 at most). An empty page ends it. The list
|
|
70
|
+
* is the whole table — far down it holds apps with nothing published — so a grid drops every
|
|
71
|
+
* app whose `versions()` comes back empty.
|
|
72
|
+
*/
|
|
73
|
+
list: async (params = {}) => {
|
|
74
|
+
const query = new URLSearchParams({
|
|
75
|
+
limit: String(Math.min(Math.max(1, params.limit ?? LIST_LIMIT_MAX), LIST_LIMIT_MAX)),
|
|
76
|
+
offset: String(Math.max(0, params.offset ?? 0))
|
|
77
|
+
});
|
|
78
|
+
if (params.category)
|
|
79
|
+
query.set('category', params.category);
|
|
80
|
+
// The body is `{ message, data: [...] }` with no `success` key, so it is not the envelope and
|
|
81
|
+
// arrives whole as `data`.
|
|
82
|
+
const { data } = await (0, http_js_1.request)({
|
|
83
|
+
method: 'GET',
|
|
84
|
+
url: `${CATALOG.list}?${query}`,
|
|
85
|
+
fetchImpl: this.transport
|
|
86
|
+
});
|
|
87
|
+
const rows = Array.isArray(data) ? data : Array.isArray(data?.data) ? data.data : [];
|
|
88
|
+
return rows.map((row) => ({
|
|
89
|
+
serviceId: String(row.rowid ?? ''),
|
|
90
|
+
name: String(row.name ?? ''),
|
|
91
|
+
description: String(row.description ?? ''),
|
|
92
|
+
iconUrl: String(row.iconurl ?? ''),
|
|
93
|
+
category: Array.isArray(row.category) ? row.category : [],
|
|
94
|
+
domain: typeof row.domain === 'string' ? row.domain : undefined
|
|
95
|
+
}));
|
|
96
|
+
},
|
|
97
|
+
/**
|
|
98
|
+
* Every published action and trigger of one app, with the schema (`inputjson`) a form is
|
|
99
|
+
* rendered from and the version id the run and subscribe calls take. Empty for an app with
|
|
100
|
+
* nothing published. Cache it per app; ids are stable and schemas change rarely.
|
|
101
|
+
*/
|
|
102
|
+
versions: async (serviceId) => {
|
|
103
|
+
const { data } = await (0, http_js_1.request)({
|
|
104
|
+
method: 'POST',
|
|
105
|
+
url: CATALOG.versions,
|
|
106
|
+
body: JSON.stringify({ service_id: required('serviceId', serviceId) }),
|
|
107
|
+
fetchImpl: this.transport
|
|
108
|
+
});
|
|
109
|
+
const rows = Array.isArray(data) ? data : [];
|
|
110
|
+
return { actions: rows.filter((row) => row.type === 'action'), triggers: rows.filter((row) => row.type === 'trigger') };
|
|
111
|
+
}
|
|
112
|
+
};
|
|
45
113
|
/** Everything that is done for one of your end users. */
|
|
46
114
|
user(uniqueIdentifier) {
|
|
47
115
|
return new UserScope(this, required('uniqueIdentifier', uniqueIdentifier));
|
package/dist/esm/browser.d.ts
CHANGED
|
@@ -27,9 +27,21 @@ export interface ConnectOptions {
|
|
|
27
27
|
embedToken: string;
|
|
28
28
|
/** The app to connect. Its service_id is shown on every app in the Apps API reference. */
|
|
29
29
|
serviceId: string;
|
|
30
|
+
/**
|
|
31
|
+
* The rowids of the actions and triggers this product uses (each is listed with its action in
|
|
32
|
+
* the app's document). The popup then asks only for their scopes. Omit to offer every action.
|
|
33
|
+
*/
|
|
34
|
+
actions?: string[];
|
|
35
|
+
/** With `actions`: skip the action list and open the app's consent screen directly. */
|
|
36
|
+
skipActionSelection?: boolean;
|
|
30
37
|
/** Where the connect script is served from. Only override for a non-production environment. */
|
|
31
38
|
scriptUrl?: string;
|
|
32
39
|
}
|
|
40
|
+
/** The third argument of the connect script's `openViasocketConnection`. */
|
|
41
|
+
interface ConnectScriptOptions {
|
|
42
|
+
filteredActions?: string[];
|
|
43
|
+
skipActionSelection?: boolean;
|
|
44
|
+
}
|
|
33
45
|
export interface ConnectResult {
|
|
34
46
|
/** The connection. Every later call for this app takes it as auth_id. */
|
|
35
47
|
authId: string;
|
|
@@ -39,7 +51,7 @@ export interface ConnectResult {
|
|
|
39
51
|
}
|
|
40
52
|
declare global {
|
|
41
53
|
interface Window {
|
|
42
|
-
openViasocketConnection?: (embedToken: string, serviceId: string) => void;
|
|
54
|
+
openViasocketConnection?: (embedToken: string, serviceId: string, options?: ConnectScriptOptions) => void;
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
57
|
/**
|
|
@@ -54,3 +66,4 @@ export declare function loadConnectScript(scriptUrl?: string): Promise<void>;
|
|
|
54
66
|
* 'rejected' if the app said no, 'script' if the connect script could not load.
|
|
55
67
|
*/
|
|
56
68
|
export declare function connect(options: ConnectOptions): Promise<ConnectResult>;
|
|
69
|
+
export {};
|
package/dist/esm/browser.js
CHANGED
|
@@ -95,6 +95,14 @@ export async function connect(options) {
|
|
|
95
95
|
}
|
|
96
96
|
};
|
|
97
97
|
window.addEventListener('message', onMessage);
|
|
98
|
-
|
|
98
|
+
const scriptOptions = {};
|
|
99
|
+
if (options.actions?.length)
|
|
100
|
+
scriptOptions.filteredActions = options.actions;
|
|
101
|
+
if (options.skipActionSelection)
|
|
102
|
+
scriptOptions.skipActionSelection = true;
|
|
103
|
+
if (Object.keys(scriptOptions).length)
|
|
104
|
+
open(options.embedToken, serviceId, scriptOptions);
|
|
105
|
+
else
|
|
106
|
+
open(options.embedToken, serviceId);
|
|
99
107
|
});
|
|
100
108
|
}
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* everything else behind `user(id)` makes that distinction structural rather than something a
|
|
8
8
|
* reader has to remember.
|
|
9
9
|
*/
|
|
10
|
-
import type { EnableResult, Flow, FlowStatus, ListOptionsParams, ListOptionsResult, SubscribeParams, SubscribeResult, UpdateSubscriptionParams, ViaSocketOptions } from './types.js';
|
|
10
|
+
import type { CatalogApp, CatalogVersions, EnableResult, Flow, FlowStatus, ListOptionsParams, ListAppsParams, ListOptionsResult, SubscribeParams, SubscribeResult, UpdateSubscriptionParams, ViaSocketOptions } from './types.js';
|
|
11
11
|
export declare class ViaSocket {
|
|
12
12
|
readonly orgId: string;
|
|
13
13
|
readonly projectId: string;
|
|
@@ -15,6 +15,26 @@ export declare class ViaSocket {
|
|
|
15
15
|
readonly runBaseUrl: string;
|
|
16
16
|
private readonly secret;
|
|
17
17
|
constructor(options: ViaSocketOptions);
|
|
18
|
+
/**
|
|
19
|
+
* The catalog: which apps exist and what each can do. Public, no token, nothing per user — so
|
|
20
|
+
* it lives on the root client and is safe to call from a browser too.
|
|
21
|
+
*/
|
|
22
|
+
readonly catalog: {
|
|
23
|
+
/** The best 30 apps for what the user typed. A type-ahead: an empty key returns nothing. */
|
|
24
|
+
search: (key: string) => Promise<CatalogApp[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Every app, most used first, a page at a time (200 at most). An empty page ends it. The list
|
|
27
|
+
* is the whole table — far down it holds apps with nothing published — so a grid drops every
|
|
28
|
+
* app whose `versions()` comes back empty.
|
|
29
|
+
*/
|
|
30
|
+
list: (params?: ListAppsParams) => Promise<CatalogApp[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Every published action and trigger of one app, with the schema (`inputjson`) a form is
|
|
33
|
+
* rendered from and the version id the run and subscribe calls take. Empty for an app with
|
|
34
|
+
* nothing published. Cache it per app; ids are stable and schemas change rarely.
|
|
35
|
+
*/
|
|
36
|
+
versions: (serviceId: string) => Promise<CatalogVersions>;
|
|
37
|
+
};
|
|
18
38
|
/** Everything that is done for one of your end users. */
|
|
19
39
|
user(uniqueIdentifier: string): UserScope;
|
|
20
40
|
/**
|
package/dist/esm/client.js
CHANGED
|
@@ -12,6 +12,13 @@ import { request } from './http.js';
|
|
|
12
12
|
import { signEmbedToken } from './token.js';
|
|
13
13
|
const DEFAULT_API_BASE_URL = 'https://flow-api.viasocket.com';
|
|
14
14
|
const DEFAULT_RUN_BASE_URL = 'https://flow.sokt.io';
|
|
15
|
+
/** The public catalog: no token, the same data in every environment. */
|
|
16
|
+
const CATALOG = {
|
|
17
|
+
search: 'https://flow.sokt.io/func/scri12BSufQM',
|
|
18
|
+
list: 'https://plug-service.viasocket.com/api/v1/plugins/all',
|
|
19
|
+
versions: 'https://flow.sokt.io/func/scriolZue69X'
|
|
20
|
+
};
|
|
21
|
+
const LIST_LIMIT_MAX = 200;
|
|
15
22
|
function required(name, value) {
|
|
16
23
|
if (typeof value !== 'string' || !value.trim())
|
|
17
24
|
throw new ViaSocketError(`${name} is required`, { status: null });
|
|
@@ -39,6 +46,67 @@ export class ViaSocket {
|
|
|
39
46
|
// Bound so a bare `fetch` reference still has the right `this` on runtimes that care.
|
|
40
47
|
this.transport = options.fetch ? transport : transport.bind(globalThis);
|
|
41
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* The catalog: which apps exist and what each can do. Public, no token, nothing per user — so
|
|
51
|
+
* it lives on the root client and is safe to call from a browser too.
|
|
52
|
+
*/
|
|
53
|
+
catalog = {
|
|
54
|
+
/** The best 30 apps for what the user typed. A type-ahead: an empty key returns nothing. */
|
|
55
|
+
search: async (key) => {
|
|
56
|
+
const url = `${CATALOG.search}?key=${encodeURIComponent(required('key', key))}`;
|
|
57
|
+
const { data } = await request({ method: 'GET', url, fetchImpl: this.transport });
|
|
58
|
+
return (Array.isArray(data) ? data : []).map((row) => ({
|
|
59
|
+
serviceId: row.service_id,
|
|
60
|
+
name: row.name,
|
|
61
|
+
description: row.description ?? '',
|
|
62
|
+
iconUrl: row.iconurl ?? ''
|
|
63
|
+
}));
|
|
64
|
+
},
|
|
65
|
+
/**
|
|
66
|
+
* Every app, most used first, a page at a time (200 at most). An empty page ends it. The list
|
|
67
|
+
* is the whole table — far down it holds apps with nothing published — so a grid drops every
|
|
68
|
+
* app whose `versions()` comes back empty.
|
|
69
|
+
*/
|
|
70
|
+
list: async (params = {}) => {
|
|
71
|
+
const query = new URLSearchParams({
|
|
72
|
+
limit: String(Math.min(Math.max(1, params.limit ?? LIST_LIMIT_MAX), LIST_LIMIT_MAX)),
|
|
73
|
+
offset: String(Math.max(0, params.offset ?? 0))
|
|
74
|
+
});
|
|
75
|
+
if (params.category)
|
|
76
|
+
query.set('category', params.category);
|
|
77
|
+
// The body is `{ message, data: [...] }` with no `success` key, so it is not the envelope and
|
|
78
|
+
// arrives whole as `data`.
|
|
79
|
+
const { data } = await request({
|
|
80
|
+
method: 'GET',
|
|
81
|
+
url: `${CATALOG.list}?${query}`,
|
|
82
|
+
fetchImpl: this.transport
|
|
83
|
+
});
|
|
84
|
+
const rows = Array.isArray(data) ? data : Array.isArray(data?.data) ? data.data : [];
|
|
85
|
+
return rows.map((row) => ({
|
|
86
|
+
serviceId: String(row.rowid ?? ''),
|
|
87
|
+
name: String(row.name ?? ''),
|
|
88
|
+
description: String(row.description ?? ''),
|
|
89
|
+
iconUrl: String(row.iconurl ?? ''),
|
|
90
|
+
category: Array.isArray(row.category) ? row.category : [],
|
|
91
|
+
domain: typeof row.domain === 'string' ? row.domain : undefined
|
|
92
|
+
}));
|
|
93
|
+
},
|
|
94
|
+
/**
|
|
95
|
+
* Every published action and trigger of one app, with the schema (`inputjson`) a form is
|
|
96
|
+
* rendered from and the version id the run and subscribe calls take. Empty for an app with
|
|
97
|
+
* nothing published. Cache it per app; ids are stable and schemas change rarely.
|
|
98
|
+
*/
|
|
99
|
+
versions: async (serviceId) => {
|
|
100
|
+
const { data } = await request({
|
|
101
|
+
method: 'POST',
|
|
102
|
+
url: CATALOG.versions,
|
|
103
|
+
body: JSON.stringify({ service_id: required('serviceId', serviceId) }),
|
|
104
|
+
fetchImpl: this.transport
|
|
105
|
+
});
|
|
106
|
+
const rows = Array.isArray(data) ? data : [];
|
|
107
|
+
return { actions: rows.filter((row) => row.type === 'action'), triggers: rows.filter((row) => row.type === 'trigger') };
|
|
108
|
+
}
|
|
109
|
+
};
|
|
42
110
|
/** Everything that is done for one of your end users. */
|
|
43
111
|
user(uniqueIdentifier) {
|
|
44
112
|
return new UserScope(this, required('uniqueIdentifier', uniqueIdentifier));
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -18,4 +18,4 @@ export { ViaSocket, UserScope } from './client.js';
|
|
|
18
18
|
export { ViaSocketError } from './errors.js';
|
|
19
19
|
export { signEmbedToken } from './token.js';
|
|
20
20
|
export type { SignEmbedTokenInput } from './token.js';
|
|
21
|
-
export type { EnableResult, Envelope, Flow, FlowStatus, ListOptionsParams, ListOptionsResult, Option, SubscribeParams, SubscribeResult, UpdateSubscriptionParams, ViaSocketOptions } from './types.js';
|
|
21
|
+
export type { CatalogApp, CatalogVersion, CatalogVersions, EnableResult, Envelope, Flow, FlowStatus, ListAppsParams, ListOptionsParams, ListOptionsResult, Option, SubscribeParams, SubscribeResult, UpdateSubscriptionParams, ViaSocketOptions } from './types.js';
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -98,6 +98,43 @@ export interface Flow {
|
|
|
98
98
|
}
|
|
99
99
|
export type FlowStatus = 0 | 1;
|
|
100
100
|
/** The envelope every viaSocket endpoint answers with. */
|
|
101
|
+
/** One app of the catalog, from a search or the full list. */
|
|
102
|
+
export interface CatalogApp {
|
|
103
|
+
/** The `service_id` every other call takes. */
|
|
104
|
+
serviceId: string;
|
|
105
|
+
name: string;
|
|
106
|
+
description: string;
|
|
107
|
+
iconUrl: string;
|
|
108
|
+
/** Only the full list carries these. */
|
|
109
|
+
category?: string[];
|
|
110
|
+
domain?: string;
|
|
111
|
+
}
|
|
112
|
+
export interface ListAppsParams {
|
|
113
|
+
/** Up to 200, the API's cap. */
|
|
114
|
+
limit?: number;
|
|
115
|
+
offset?: number;
|
|
116
|
+
/** One of the catalog's category names, e.g. "CRM". */
|
|
117
|
+
category?: string;
|
|
118
|
+
}
|
|
119
|
+
/** One published action or trigger version of an app, as the catalog returns it. */
|
|
120
|
+
export interface CatalogVersion {
|
|
121
|
+
/** The `action_version_id` (or trigger version id) the run and subscribe calls take. */
|
|
122
|
+
actionversionrecordid: string;
|
|
123
|
+
type: 'action' | 'trigger' | string;
|
|
124
|
+
name: string;
|
|
125
|
+
description: string;
|
|
126
|
+
/** The field schema a form is rendered from; `inputData` follows it. */
|
|
127
|
+
inputjson: Record<string, unknown>;
|
|
128
|
+
sampledata?: unknown;
|
|
129
|
+
pluginrecordid?: string;
|
|
130
|
+
iconurl?: string;
|
|
131
|
+
authtype?: string;
|
|
132
|
+
[key: string]: unknown;
|
|
133
|
+
}
|
|
134
|
+
export interface CatalogVersions {
|
|
135
|
+
actions: CatalogVersion[];
|
|
136
|
+
triggers: CatalogVersion[];
|
|
137
|
+
}
|
|
101
138
|
export interface Envelope<T = unknown> {
|
|
102
139
|
success: boolean;
|
|
103
140
|
message?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viasocket-apps",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|