flowstack-sdk 0.2.1 → 0.2.3

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
@@ -1,5 +1,10 @@
1
1
  # Flowstack SDK
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/flowstack-sdk.svg)](https://www.npmjs.com/package/flowstack-sdk)
4
+ [![npm downloads/week](https://img.shields.io/npm/dw/flowstack-sdk.svg)](https://www.npmjs.com/package/flowstack-sdk)
5
+ [![npm total downloads](https://img.shields.io/npm/dt/flowstack-sdk.svg)](https://www.npmjs.com/package/flowstack-sdk)
6
+ [![license](https://img.shields.io/npm/l/flowstack-sdk.svg)](https://www.npmjs.com/package/flowstack-sdk)
7
+
3
8
  Complete Backend-as-a-Service SDK for AI-powered applications. Build full-stack AI apps with authentication, workspace management, dataset handling, AI agent integration, published sites, and wallet-based payments — all in one package.
4
9
 
5
10
  ```bash
@@ -1553,6 +1558,83 @@ Return value:
1553
1558
 
1554
1559
  ---
1555
1560
 
1561
+ ## Private Messaging (DMs)
1562
+
1563
+ Built-app users can exchange private, one-to-one messages. The DM store is **server-owned and ACL'd**: the backend pins `from` to the caller's JWT identity, only ever returns threads the caller participates in, and gates sends on a **mutually-consented (open) thread** — neither party ever learns the other's contact details.
1564
+
1565
+ Private messaging is a **built-app capability**. It requires an `appScope` on the `FlowstackProvider` config; in a Casino personal session the backend returns 403. All symbols below ship in **0.2.2+**.
1566
+
1567
+ > **Security:** message bodies are UNTRUSTED user input. Render them as plain text or sanitized markdown — never as raw HTML (no `dangerouslySetInnerHTML`). If a body is ever passed to an agent, treat it as data, not instructions.
1568
+
1569
+ ### useThreads — list a user's threads
1570
+
1571
+ ```tsx
1572
+ import { useThreads } from 'flowstack-sdk';
1573
+
1574
+ const { threads, isLoading, error, refresh, openThread } = useThreads({
1575
+ refreshInterval: 5000, // optional auto-poll (ms); off by default
1576
+ });
1577
+
1578
+ // Each thread: { pair_key, with_user_key, status: 'pending' | 'open', last_message, unread_count, updated_at }
1579
+ threads.map((t) => <ThreadRow key={t.pair_key} counterpart={t.with_user_key} unread={t.unread_count} />);
1580
+
1581
+ // Consent to open a thread — becomes sendable only once BOTH parties consent.
1582
+ const status = await openThread(otherUserKey); // 'pending' | 'open' | null
1583
+ ```
1584
+
1585
+ ### useMessages — read + send within one thread
1586
+
1587
+ ```tsx
1588
+ import { useMessages } from 'flowstack-sdk';
1589
+
1590
+ const { messages, send, isLoading, error, refresh } = useMessages(counterpartUserKey, {
1591
+ limit: 50, // default 50, max 200
1592
+ refreshInterval: 4000,
1593
+ });
1594
+
1595
+ await send('hey there'); // 403 / error if the thread is not yet mutually open
1596
+ messages.map((m) => <Bubble key={m.message_id} mine={m.from === myKey}>{m.body}</Bubble>);
1597
+ ```
1598
+
1599
+ ### Client functions (non-React)
1600
+
1601
+ All take `(credentials, …, config)` where `config` must carry `appScope`. Each returns an `ApiResponse<T>` (`{ ok, data, error }`), except `dmPairKey` which is a pure helper.
1602
+
1603
+ ```ts
1604
+ import {
1605
+ listThreads, // (creds, config) → { threads: DmThread[]; count }
1606
+ listMessages, // (creds, withUserKey, { limit?, before? }, config) → { messages: DmMessage[]; count }
1607
+ sendMessage, // (creds, toUserKey, body, config) → { message_id, created_at } — 403 unless thread open
1608
+ openThread, // (creds, withUserKey, config) → { pair_key, status } — record caller's consent
1609
+ markMessageRead, // (creds, messageId, config) → { message_id, read } — recipient only
1610
+ dmPairKey, // (a, b) → string — deterministic, order-independent thread key (sorted, '::'-joined)
1611
+ } from 'flowstack-sdk';
1612
+ ```
1613
+
1614
+ **Types:**
1615
+
1616
+ ```ts
1617
+ interface DmMessage {
1618
+ message_id: string;
1619
+ from: string;
1620
+ to: string;
1621
+ body: string; // UNTRUSTED — render as text, never raw HTML
1622
+ created_at: string;
1623
+ read_at: string | null;
1624
+ }
1625
+
1626
+ interface DmThread {
1627
+ pair_key: string;
1628
+ with_user_key: string;
1629
+ status: 'pending' | 'open';
1630
+ last_message: DmMessage | null;
1631
+ unread_count: number;
1632
+ updated_at: string | null;
1633
+ }
1634
+ ```
1635
+
1636
+ ---
1637
+
1556
1638
  ## Components
1557
1639
 
1558
1640
  ### Pre-built Page Components
@@ -1 +1 @@
1
- export { bM as CACHE_TTL, c7 as CreateSiteParams, N as FlowstackClientConfig, c8 as RedisConfig, bL as RequestOptions, bD as addPiiAllowlistTerm, bk as addSiteFile, be as checkAdminPermissions, aZ as createDataSource, bj as createSite, aN as createWorkspace, c4 as deleteCached, a$ as deleteDataSource, aS as deleteDataset, bv as deleteDocuments, bm as deleteSite, bp as deleteSiteVersion, b9 as deleteUser, bJ as deleteUserCollection, b0 as executeQuery, b1 as executeQueryWithConfig, bK as exportUserCollection, aL as flowstackFetch, c2 as getCached, bQ as getCachedDatasets, bW as getCachedReports, b$ as getCachedSites, bT as getCachedVisualizations, bN as getCachedWorkspaces, bf as getConversationHistory, aQ as getDataset, aR as getDatasetPreview, aX as getModel, bC as getPiiAllowlist, bz as getPiiSettings, bi as getSite, bn as getSiteVersions, b7 as getUser, bc as getUserActivity, bH as getUserCollectionDocuments, bI as getUserCollectionSchema, bG as getUserCollections, bF as getUserDataOverview, bd as getUserStats, aO as getWorkspace, b5 as googleLogin, by as importFromGitHub, bt as insertDocuments, b_ as invalidateAllUserCache, bS as invalidateDatasetsCache, bY as invalidateReportsCache, c1 as invalidateSitesCache, bV as invalidateVisualizationsCache, bZ as invalidateWorkspaceArtifacts, bP as invalidateWorkspacesCache, bw as invokeTool, bg as listAgents, aY as listDataSources, aP as listDatasets, bx as listGitHubRepos, aV as listModels, aU as listReports, aW as listScripts, bh as listSites, b6 as listUsers, aT as listVisualizations, aM as listWorkspaces, b3 as login, bB as previewPiiMasking, bo as promoteSiteVersion, bl as publishStagedSite, bs as publishToGitHub, c5 as queryCollection, bb as reactivateUser, b4 as register, bE as removePiiAllowlistTerm, br as removeSiteAlias, c3 as setCached, bR as setCachedDatasets, bX as setCachedReports, c0 as setCachedSites, bU as setCachedVisualizations, bO as setCachedWorkspaces, bq as setSiteAlias, ba as suspendUser, a_ as testDataSource, bu as updateDocuments, bA as updatePiiSettings, b8 as updateUser, c6 as uploadDocument, b2 as uploadFile } from '../index-BkACA2ls.mjs';
1
+ export { bU as CACHE_TTL, cf as CreateSiteParams, o as DmMessage, D as DmThread, R as FlowstackClientConfig, cg as RedisConfig, bT as RequestOptions, bL as addPiiAllowlistTerm, bm as addSiteFile, bg as checkAdminPermissions, a$ as createDataSource, bl as createSite, aP as createWorkspace, cc as deleteCached, b1 as deleteDataSource, aU as deleteDataset, bx as deleteDocuments, bo as deleteSite, br as deleteSiteVersion, bb as deleteUser, bR as deleteUserCollection, bD as dmPairKey, b2 as executeQuery, b3 as executeQueryWithConfig, bS as exportUserCollection, aN as flowstackFetch, ca as getCached, bY as getCachedDatasets, c2 as getCachedReports, c7 as getCachedSites, b$ as getCachedVisualizations, bV as getCachedWorkspaces, bh as getConversationHistory, aS as getDataset, aT as getDatasetPreview, aZ as getModel, bK as getPiiAllowlist, bH as getPiiSettings, bk as getSite, bp as getSiteVersions, b9 as getUser, be as getUserActivity, bP as getUserCollectionDocuments, bQ as getUserCollectionSchema, bO as getUserCollections, bN as getUserDataOverview, bf as getUserStats, aQ as getWorkspace, b7 as googleLogin, bG as importFromGitHub, bv as insertDocuments, c6 as invalidateAllUserCache, b_ as invalidateDatasetsCache, c4 as invalidateReportsCache, c9 as invalidateSitesCache, c1 as invalidateVisualizationsCache, c5 as invalidateWorkspaceArtifacts, bX as invalidateWorkspacesCache, bE as invokeTool, bi as listAgents, a_ as listDataSources, aR as listDatasets, bF as listGitHubRepos, bz as listMessages, aX as listModels, aW as listReports, aY as listScripts, bj as listSites, by as listThreads, b8 as listUsers, aV as listVisualizations, aO as listWorkspaces, b5 as login, bC as markMessageRead, bB as openThread, bJ as previewPiiMasking, bq as promoteSiteVersion, bn as publishStagedSite, bu as publishToGitHub, cd as queryCollection, bd as reactivateUser, b6 as register, bM as removePiiAllowlistTerm, bt as removeSiteAlias, bA as sendMessage, cb as setCached, bZ as setCachedDatasets, c3 as setCachedReports, c8 as setCachedSites, c0 as setCachedVisualizations, bW as setCachedWorkspaces, bs as setSiteAlias, bc as suspendUser, b0 as testDataSource, bw as updateDocuments, bI as updatePiiSettings, ba as updateUser, ce as uploadDocument, b4 as uploadFile } from '../index-CUyJ5c2d.mjs';
@@ -1 +1 @@
1
- export { bM as CACHE_TTL, c7 as CreateSiteParams, N as FlowstackClientConfig, c8 as RedisConfig, bL as RequestOptions, bD as addPiiAllowlistTerm, bk as addSiteFile, be as checkAdminPermissions, aZ as createDataSource, bj as createSite, aN as createWorkspace, c4 as deleteCached, a$ as deleteDataSource, aS as deleteDataset, bv as deleteDocuments, bm as deleteSite, bp as deleteSiteVersion, b9 as deleteUser, bJ as deleteUserCollection, b0 as executeQuery, b1 as executeQueryWithConfig, bK as exportUserCollection, aL as flowstackFetch, c2 as getCached, bQ as getCachedDatasets, bW as getCachedReports, b$ as getCachedSites, bT as getCachedVisualizations, bN as getCachedWorkspaces, bf as getConversationHistory, aQ as getDataset, aR as getDatasetPreview, aX as getModel, bC as getPiiAllowlist, bz as getPiiSettings, bi as getSite, bn as getSiteVersions, b7 as getUser, bc as getUserActivity, bH as getUserCollectionDocuments, bI as getUserCollectionSchema, bG as getUserCollections, bF as getUserDataOverview, bd as getUserStats, aO as getWorkspace, b5 as googleLogin, by as importFromGitHub, bt as insertDocuments, b_ as invalidateAllUserCache, bS as invalidateDatasetsCache, bY as invalidateReportsCache, c1 as invalidateSitesCache, bV as invalidateVisualizationsCache, bZ as invalidateWorkspaceArtifacts, bP as invalidateWorkspacesCache, bw as invokeTool, bg as listAgents, aY as listDataSources, aP as listDatasets, bx as listGitHubRepos, aV as listModels, aU as listReports, aW as listScripts, bh as listSites, b6 as listUsers, aT as listVisualizations, aM as listWorkspaces, b3 as login, bB as previewPiiMasking, bo as promoteSiteVersion, bl as publishStagedSite, bs as publishToGitHub, c5 as queryCollection, bb as reactivateUser, b4 as register, bE as removePiiAllowlistTerm, br as removeSiteAlias, c3 as setCached, bR as setCachedDatasets, bX as setCachedReports, c0 as setCachedSites, bU as setCachedVisualizations, bO as setCachedWorkspaces, bq as setSiteAlias, ba as suspendUser, a_ as testDataSource, bu as updateDocuments, bA as updatePiiSettings, b8 as updateUser, c6 as uploadDocument, b2 as uploadFile } from '../index-BkACA2ls.js';
1
+ export { bU as CACHE_TTL, cf as CreateSiteParams, o as DmMessage, D as DmThread, R as FlowstackClientConfig, cg as RedisConfig, bT as RequestOptions, bL as addPiiAllowlistTerm, bm as addSiteFile, bg as checkAdminPermissions, a$ as createDataSource, bl as createSite, aP as createWorkspace, cc as deleteCached, b1 as deleteDataSource, aU as deleteDataset, bx as deleteDocuments, bo as deleteSite, br as deleteSiteVersion, bb as deleteUser, bR as deleteUserCollection, bD as dmPairKey, b2 as executeQuery, b3 as executeQueryWithConfig, bS as exportUserCollection, aN as flowstackFetch, ca as getCached, bY as getCachedDatasets, c2 as getCachedReports, c7 as getCachedSites, b$ as getCachedVisualizations, bV as getCachedWorkspaces, bh as getConversationHistory, aS as getDataset, aT as getDatasetPreview, aZ as getModel, bK as getPiiAllowlist, bH as getPiiSettings, bk as getSite, bp as getSiteVersions, b9 as getUser, be as getUserActivity, bP as getUserCollectionDocuments, bQ as getUserCollectionSchema, bO as getUserCollections, bN as getUserDataOverview, bf as getUserStats, aQ as getWorkspace, b7 as googleLogin, bG as importFromGitHub, bv as insertDocuments, c6 as invalidateAllUserCache, b_ as invalidateDatasetsCache, c4 as invalidateReportsCache, c9 as invalidateSitesCache, c1 as invalidateVisualizationsCache, c5 as invalidateWorkspaceArtifacts, bX as invalidateWorkspacesCache, bE as invokeTool, bi as listAgents, a_ as listDataSources, aR as listDatasets, bF as listGitHubRepos, bz as listMessages, aX as listModels, aW as listReports, aY as listScripts, bj as listSites, by as listThreads, b8 as listUsers, aV as listVisualizations, aO as listWorkspaces, b5 as login, bC as markMessageRead, bB as openThread, bJ as previewPiiMasking, bq as promoteSiteVersion, bn as publishStagedSite, bu as publishToGitHub, cd as queryCollection, bd as reactivateUser, b6 as register, bM as removePiiAllowlistTerm, bt as removeSiteAlias, bA as sendMessage, cb as setCached, bZ as setCachedDatasets, c3 as setCachedReports, c8 as setCachedSites, c0 as setCachedVisualizations, bW as setCachedWorkspaces, bs as setSiteAlias, bc as suspendUser, b0 as testDataSource, bw as updateDocuments, bI as updatePiiSettings, ba as updateUser, ce as uploadDocument, b4 as uploadFile } from '../index-CUyJ5c2d.js';
package/dist/api/index.js CHANGED
@@ -264,7 +264,25 @@ async function executeQueryWithConfig(credentials, query, workspaceId, options,
264
264
  })
265
265
  });
266
266
  if (!response.ok) {
267
- throw new Error(`Query failed: ${response.statusText}`);
267
+ let message = response.statusText || "request failed";
268
+ let code;
269
+ let body;
270
+ try {
271
+ body = await response.clone().json();
272
+ const payload = body && typeof body === "object" && "detail" in body && typeof body.detail === "object" ? body.detail : body;
273
+ if (payload && typeof payload === "object") {
274
+ code = payload.code;
275
+ message = payload.error || payload.message || payload.detail || message;
276
+ } else if (typeof payload === "string") {
277
+ message = payload;
278
+ }
279
+ } catch {
280
+ }
281
+ const err = new Error(`Query failed: ${message}`);
282
+ err.status = response.status;
283
+ err.code = code;
284
+ err.body = body;
285
+ throw err;
268
286
  }
269
287
  return response;
270
288
  }
@@ -332,6 +350,63 @@ async function deleteDocuments(credentials, collection, filter, config, layer) {
332
350
  config
333
351
  );
334
352
  }
353
+ function dmPairKey(a, b) {
354
+ return [a, b].sort().join("::");
355
+ }
356
+ function requireAppScope(config) {
357
+ const scope = config?.appScope;
358
+ if (!scope) {
359
+ throw new Error("Private messaging requires an app scope (built-app context).");
360
+ }
361
+ return scope;
362
+ }
363
+ async function listThreads(credentials, config) {
364
+ const scope = requireAppScope(config);
365
+ return flowstackFetch(
366
+ `/apps/${encodeURIComponent(scope)}/threads`,
367
+ { credentials },
368
+ config
369
+ );
370
+ }
371
+ async function listMessages(credentials, withUserKey, options, config) {
372
+ const scope = requireAppScope(config);
373
+ const params = new URLSearchParams();
374
+ params.set("with", withUserKey);
375
+ if (options?.limit) params.set("limit", String(options.limit));
376
+ if (options?.before) params.set("before", options.before);
377
+ return flowstackFetch(
378
+ `/apps/${encodeURIComponent(scope)}/messages?${params.toString()}`,
379
+ { credentials },
380
+ config
381
+ );
382
+ }
383
+ async function sendMessage(credentials, toUserKey, body, config) {
384
+ const scope = requireAppScope(config);
385
+ return flowstackFetch(
386
+ `/apps/${encodeURIComponent(scope)}/messages`,
387
+ { method: "POST", credentials, body: { to_user_key: toUserKey, body } },
388
+ config
389
+ );
390
+ }
391
+ async function openThread(credentials, withUserKey, config) {
392
+ const scope = requireAppScope(config);
393
+ const me = credentials.userId;
394
+ if (!me) throw new Error("openThread requires an authenticated user.");
395
+ const pk = dmPairKey(me, withUserKey);
396
+ return flowstackFetch(
397
+ `/apps/${encodeURIComponent(scope)}/threads/${encodeURIComponent(pk)}/consent`,
398
+ { method: "POST", credentials },
399
+ config
400
+ );
401
+ }
402
+ async function markMessageRead(credentials, messageId, config) {
403
+ const scope = requireAppScope(config);
404
+ return flowstackFetch(
405
+ `/apps/${encodeURIComponent(scope)}/messages/${encodeURIComponent(messageId)}/read`,
406
+ { method: "POST", credentials },
407
+ config
408
+ );
409
+ }
335
410
  async function invokeTool(credentials, agentName, toolName, kwargs = {}, config) {
336
411
  return flowstackFetch(
337
412
  "/tool/invoke",
@@ -989,6 +1064,7 @@ exports.deleteSite = deleteSite;
989
1064
  exports.deleteSiteVersion = deleteSiteVersion;
990
1065
  exports.deleteUser = deleteUser;
991
1066
  exports.deleteUserCollection = deleteUserCollection;
1067
+ exports.dmPairKey = dmPairKey;
992
1068
  exports.executeQuery = executeQuery;
993
1069
  exports.executeQueryWithConfig = executeQueryWithConfig;
994
1070
  exports.exportUserCollection = exportUserCollection;
@@ -1030,14 +1106,18 @@ exports.listAgents = listAgents;
1030
1106
  exports.listDataSources = listDataSources;
1031
1107
  exports.listDatasets = listDatasets;
1032
1108
  exports.listGitHubRepos = listGitHubRepos;
1109
+ exports.listMessages = listMessages;
1033
1110
  exports.listModels = listModels;
1034
1111
  exports.listReports = listReports;
1035
1112
  exports.listScripts = listScripts;
1036
1113
  exports.listSites = listSites;
1114
+ exports.listThreads = listThreads;
1037
1115
  exports.listUsers = listUsers;
1038
1116
  exports.listVisualizations = listVisualizations;
1039
1117
  exports.listWorkspaces = listWorkspaces;
1040
1118
  exports.login = login;
1119
+ exports.markMessageRead = markMessageRead;
1120
+ exports.openThread = openThread;
1041
1121
  exports.previewPiiMasking = previewPiiMasking;
1042
1122
  exports.promoteSiteVersion = promoteSiteVersion;
1043
1123
  exports.publishStagedSite = publishStagedSite;
@@ -1047,6 +1127,7 @@ exports.reactivateUser = reactivateUser;
1047
1127
  exports.register = register;
1048
1128
  exports.removePiiAllowlistTerm = removePiiAllowlistTerm;
1049
1129
  exports.removeSiteAlias = removeSiteAlias;
1130
+ exports.sendMessage = sendMessage;
1050
1131
  exports.setCached = setCached;
1051
1132
  exports.setCachedDatasets = setCachedDatasets;
1052
1133
  exports.setCachedReports = setCachedReports;