@lotics/app-sdk 0.9.0 → 0.11.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.
@@ -0,0 +1 @@
1
+ export declare function downloadFileFromUrl(url: string, filename: string): Promise<void>;
@@ -0,0 +1,30 @@
1
+ // File download primitive for Lotics custom-code apps. Uses fetch+blob+anchor
2
+ // because the app runs inside a sandboxed iframe — `window.open(url, "_blank")`
3
+ // is silently dropped without `allow-popups`, and direct anchor navigation
4
+ // without the `download` attribute doesn't trigger a save dialog for inline
5
+ // MIME types (HTML, JSON, PDFs, etc.).
6
+ //
7
+ // `cache: "no-store"` matches the frontend convention — avoids CORS cache
8
+ // collisions where a prior <img> load cached the response without an
9
+ // `Access-Control-Allow-Origin` header, breaking subsequent fetches.
10
+ export async function downloadFileFromUrl(url, filename) {
11
+ if (!url)
12
+ throw new Error("downloadFileFromUrl: empty url");
13
+ const response = await fetch(url, { cache: "no-store" });
14
+ if (!response.ok) {
15
+ throw new Error(`File download failed: ${response.status} ${response.statusText}`);
16
+ }
17
+ const blob = await response.blob();
18
+ const blobUrl = URL.createObjectURL(blob);
19
+ try {
20
+ const link = document.createElement("a");
21
+ link.href = blobUrl;
22
+ link.download = filename;
23
+ document.body.appendChild(link);
24
+ link.click();
25
+ link.remove();
26
+ }
27
+ finally {
28
+ URL.revokeObjectURL(blobUrl);
29
+ }
30
+ }
@@ -13,7 +13,10 @@ export { mount } from "./mount.js";
13
13
  export type { MountOptions } from "./mount.js";
14
14
  export { useWorkflow, useQuery, useFileUpload } from "./hooks.js";
15
15
  export type { UploadedFile } from "./hooks.js";
16
+ export { downloadFileFromUrl } from "./download.js";
16
17
  export { rpc } from "./rpc.js";
17
18
  export type { RpcOp } from "./rpc.js";
19
+ export { readMembers } from "./members.js";
20
+ export type { ResolvedMember } from "./members.js";
18
21
  export type { AppFixture } from "./mock.js";
19
22
  export type { AppWorkflows, AppQueries } from "./types.js";
package/dist/src/index.js CHANGED
@@ -11,4 +11,6 @@
11
11
  */
12
12
  export { mount } from "./mount.js";
13
13
  export { useWorkflow, useQuery, useFileUpload } from "./hooks.js";
14
+ export { downloadFileFromUrl } from "./download.js";
14
15
  export { rpc } from "./rpc.js";
16
+ export { readMembers } from "./members.js";
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Reader for `select_member` cells in `useQuery` rows.
3
+ *
4
+ * The server (`backend/lib/select_member_resolver.ts`) rewrites every
5
+ * `select_member` column from its storage shape (`string[]` of bare member
6
+ * IDs) into `ResolvedMember[]` before the row reaches the app. Apps used to
7
+ * hardcode an id→name fallback map because the SDK didn't expose the
8
+ * resolved shape; this helper makes the right shape the obvious one.
9
+ *
10
+ * If the wire format changes, the resolver and this reader move together.
11
+ */
12
+ export interface ResolvedMember {
13
+ id: string;
14
+ /** `null` when the id resolves outside the app's org (e.g. removed
15
+ * member) — surfaces the missing state explicitly instead of an
16
+ * empty string. */
17
+ name: string | null;
18
+ /** Present only on authenticated responses. Omitted on public-app
19
+ * responses (no PII exposure to anonymous visitors). */
20
+ email?: string | null;
21
+ }
22
+ /**
23
+ * Parse a `useQuery` cell value into `ResolvedMember[]`. Returns `[]` for
24
+ * null/undefined/empty cells and for any unexpected shape — callers iterate
25
+ * uniformly without null-checks. Entries that fail the shape check are
26
+ * dropped silently rather than corrupting the array with partial data.
27
+ */
28
+ export declare function readMembers(value: unknown): ResolvedMember[];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Reader for `select_member` cells in `useQuery` rows.
3
+ *
4
+ * The server (`backend/lib/select_member_resolver.ts`) rewrites every
5
+ * `select_member` column from its storage shape (`string[]` of bare member
6
+ * IDs) into `ResolvedMember[]` before the row reaches the app. Apps used to
7
+ * hardcode an id→name fallback map because the SDK didn't expose the
8
+ * resolved shape; this helper makes the right shape the obvious one.
9
+ *
10
+ * If the wire format changes, the resolver and this reader move together.
11
+ */
12
+ /**
13
+ * Parse a `useQuery` cell value into `ResolvedMember[]`. Returns `[]` for
14
+ * null/undefined/empty cells and for any unexpected shape — callers iterate
15
+ * uniformly without null-checks. Entries that fail the shape check are
16
+ * dropped silently rather than corrupting the array with partial data.
17
+ */
18
+ export function readMembers(value) {
19
+ if (!Array.isArray(value))
20
+ return [];
21
+ const out = [];
22
+ for (const entry of value) {
23
+ if (!entry || typeof entry !== "object")
24
+ continue;
25
+ const obj = entry;
26
+ const id = obj.id;
27
+ if (typeof id !== "string" || id === "")
28
+ continue;
29
+ const name = typeof obj.name === "string" ? obj.name : obj.name === null ? null : null;
30
+ const m = { id, name };
31
+ if ("email" in obj) {
32
+ m.email =
33
+ typeof obj.email === "string"
34
+ ? obj.email
35
+ : obj.email === null
36
+ ? null
37
+ : null;
38
+ }
39
+ out.push(m);
40
+ }
41
+ return out;
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/app-sdk",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Runtime SDK for Lotics custom-code apps — typed hooks, postMessage bridge, mount entry point",
5
5
  "type": "module",
6
6
  "exports": {