@hailer/app-sdk 2.9.0 → 2.14.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/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { HailerApi } from './hailer-api';
2
2
  export * from './types';
3
3
  export * from './modules/metrics';
4
+ export * from './modules/file';
@@ -1,5 +1,5 @@
1
1
  import { Client } from '@wishcore/wish-rpc';
2
- import { ActivityCreateOptions, ActivityListOptions, Activity, ActivityFieldUpdateValue, ActivityUpdateOptions, ActivityFieldValue, PrintRequest, PrintResponse } from '../types';
2
+ import { ActivityCreateOptions, ActivityListOptions, Activity, ActivityFieldUpdateValue, ActivityUpdateOptions, ActivityFieldValue, PrintRequest, PrintOptions, PrintResponse } from '../types';
3
3
  import { Kanban } from './activity/kanban';
4
4
  import { Ball } from './activity/ball';
5
5
  export declare class ActivityApi {
@@ -76,10 +76,12 @@ export declare class ActivityApi {
76
76
  * @param requests[].templateId Template ID for regular templates
77
77
  * @param requests[].templateName Template name for multi-pdf templates (when templateId is empty)
78
78
  * @param requests[].savePdf Whether to save the generated PDF
79
+ * @param options.openNative Also open the document in the device's own viewer where the host supports it
80
+ * (the Hailer Android app); see `openedNatively` in the response
79
81
  *
80
82
  * @returns PDF data with filename, base64 data, and mimeType
81
83
  *
82
84
  * @throws {HailerError} if request is incorrect
83
85
  */
84
- print(requests: PrintRequest[]): Promise<PrintResponse>;
86
+ print(requests: PrintRequest[], options?: PrintOptions): Promise<PrintResponse>;
85
87
  }
@@ -10,9 +10,74 @@ export interface FileResponse {
10
10
  export interface UploadOptions {
11
11
  /** Whether the file should be publicly accessible */
12
12
  isPublic?: boolean;
13
+ /**
14
+ * Scope the file to the app's workspace (irreversible). A scoped file is readable only by its owner,
15
+ * workspace admins and its readers; set readers with `setReaders`. Cannot be combined with `isPublic`,
16
+ * and not available to guests. This is the only way to scope a file.
17
+ */
18
+ scoped?: boolean;
19
+ }
20
+ /** Owner, workspace and readers of a file. `workspaceId` and `readers` are absent for an unscoped file. */
21
+ export interface FileScope {
22
+ _id: string;
23
+ /** Owner of the file */
24
+ uid: string;
25
+ /** Workspace the file is scoped to */
26
+ workspaceId?: string;
27
+ /** Reader entries, see `FileApi.readers` */
28
+ readers?: string[];
29
+ }
30
+ export interface FileListOptions {
31
+ /** One keyword or up to 20. A keyword matches a case-insensitive substring of the file name or of any tag. */
32
+ search?: string | string[];
33
+ /** `all` (default) requires every keyword to match, `any` at least one */
34
+ matchType?: 'all' | 'any';
35
+ skip?: number;
36
+ /** 1-100, default 100 */
37
+ limit?: number;
38
+ sortBy?: 'name' | 'created';
39
+ reverse?: boolean;
13
40
  }
41
+ export interface ScopedFile {
42
+ _id: string;
43
+ name: string;
44
+ /** Owner of the file */
45
+ uid: string;
46
+ workspaceId: string;
47
+ type?: string;
48
+ length?: number;
49
+ created?: number;
50
+ updated?: number;
51
+ tags?: string[];
52
+ }
53
+ export interface FileListResponse {
54
+ files: ScopedFile[];
55
+ hasMore: boolean;
56
+ total: number;
57
+ }
58
+ /**
59
+ * Builders for `setReaders` entries. Only full members of the app's workspace ever match a reader:
60
+ * guests never do, not even through a team or a `user_` entry.
61
+ */
62
+ export declare const fileReaders: {
63
+ /** Every full member of the workspace */
64
+ workspace: (workspaceId: string) => string;
65
+ /** The full members of a team; guests in the team are skipped */
66
+ team: (teamId: string) => string;
67
+ /** A single full member; a guest is rejected */
68
+ user: (userId: string) => string;
69
+ };
14
70
  export declare class FileApi {
15
71
  private client;
72
+ /** Builders for `setReaders` entries: `readers.workspace(id)`, `readers.team(id)`, `readers.user(id)` */
73
+ readonly readers: {
74
+ /** Every full member of the workspace */
75
+ workspace: (workspaceId: string) => string;
76
+ /** The full members of a team; guests in the team are skipped */
77
+ team: (teamId: string) => string;
78
+ /** A single full member; a guest is rejected */
79
+ user: (userId: string) => string;
80
+ };
16
81
  constructor(client: Client);
17
82
  /**
18
83
  * Gets a file by ID and returns it as base64 data
@@ -39,7 +104,8 @@ export declare class FileApi {
39
104
  /**
40
105
  * Appends tags to a file, deduping against the existing list.
41
106
  *
42
- * Tags are case-sensitive free-form strings (trimmed, 1-64 characters, max 50 per file).
107
+ * Tags are case-sensitive free-form strings (trimmed, 1-64 characters, max 50 per file). On a scoped
108
+ * file only its owner or a workspace admin may change tags; readers can read them but not edit.
43
109
  *
44
110
  * @param fileId The ID of the file to tag
45
111
  * @param tags Tags to add
@@ -51,6 +117,7 @@ export declare class FileApi {
51
117
  addTags(fileId: string, tags: string[]): Promise<string[]>;
52
118
  /**
53
119
  * Removes tags from a file by case-sensitive exact match. Tags not present are silently ignored.
120
+ * On a scoped file only its owner or a workspace admin may change tags.
54
121
  *
55
122
  * @param fileId The ID of the file to untag
56
123
  * @param tags Tags to remove
@@ -60,5 +127,74 @@ export declare class FileApi {
60
127
  * @throws {HailerError} if validation fails or the file cannot be edited
61
128
  */
62
129
  removeTags(fileId: string, tags: string[]): Promise<string[]>;
130
+ /**
131
+ * Gets the owner, workspace and readers of a file you can read. Use it as an "is this file scoped?"
132
+ * probe: `workspaceId` is absent for an unscoped file.
133
+ *
134
+ * @param fileId The ID of the file
135
+ *
136
+ * @throws {HailerError} if the file does not exist or you cannot read it (guests cannot read scoped
137
+ * files), or if it is scoped to a workspace other than the app's (403)
138
+ */
139
+ getScope(fileId: string): Promise<FileScope>;
140
+ /**
141
+ * Replaces the readers of a file scoped to the app's workspace. Files are scoped only at upload
142
+ * (`upload(..., { scoped: true })`); an unscoped file is refused (403) rather than scoped here.
143
+ *
144
+ * Requires being the file's owner (while a full member) or a workspace admin; not available to guests.
145
+ * A non-admin may only add teams they belong to. Every entry must belong to the app's workspace, and
146
+ * `user_` must name a full member, not a guest.
147
+ *
148
+ * @param fileId The ID of the file
149
+ * @param readers The complete new reader list (max 100); build entries with `readers.*`. Send `[]` to
150
+ * leave only the owner and workspace admins.
151
+ *
152
+ * @returns The resulting scope
153
+ *
154
+ * @throws {HailerError} if validation fails, the file is not scoped to the app's workspace, or you may
155
+ * not manage it
156
+ */
157
+ setReaders(fileId: string, readers: string[]): Promise<FileScope>;
158
+ /**
159
+ * Lists the scoped files of the app's workspace that the user can read: their own, those they are a
160
+ * reader of, or all of them for a workspace admin. Unscoped files are not listed.
161
+ *
162
+ * @param options Search by name/tags and paging
163
+ *
164
+ * @returns A page of files, whether more exist, and the total match count
165
+ *
166
+ * @throws {HailerError} if validation fails, or for a guest
167
+ */
168
+ list(options?: FileListOptions): Promise<FileListResponse>;
169
+ /**
170
+ * Transfers a file scoped to the app's workspace to another full member of it (not a guest). Workspace
171
+ * admins only. The previous owner loses access unless they are a reader.
172
+ *
173
+ * @param fileId The ID of the file
174
+ * @param userId The new owner
175
+ *
176
+ * @throws {HailerError} if you are not a workspace admin, the file is not scoped to the app's workspace,
177
+ * or the user is not a full member
178
+ */
179
+ setOwner(fileId: string, userId: string): Promise<boolean>;
180
+ /**
181
+ * Permanently deletes files scoped to the app's workspace that you own. Ids of files owned by someone
182
+ * else are skipped without an error, even for a workspace admin; re-list to see what is left. If any id
183
+ * is not a file scoped to the app's workspace, the whole call is refused (403) and nothing is removed:
184
+ * an app can never delete the user's other files.
185
+ *
186
+ * @param fileIds 1-100 file ids
187
+ *
188
+ * @returns `true` once the request is processed
189
+ *
190
+ * @throws {HailerError} if validation fails, or any file is not scoped to the app's workspace
191
+ */
192
+ remove(fileIds: string[]): Promise<boolean>;
193
+ /**
194
+ * An older Hailer ignores the `scoped` upload argument and stores the file unscoped. Fail loudly rather
195
+ * than hand back a file the app believes is restricted. Only a missing `file.getScope` (wish-rpc code
196
+ * 300) or an unscoped result means an older Hailer; any other error is passed on as is.
197
+ */
198
+ private assertScoped;
63
199
  private toBase64;
64
200
  }
package/lib/types.d.ts CHANGED
@@ -428,11 +428,26 @@ export interface PrintRequest {
428
428
  /** Whether to save the PDF to the file system */
429
429
  savePdf?: boolean;
430
430
  }
431
+ export interface PrintOptions {
432
+ /**
433
+ * Open the generated document in the device's own viewer, where the host supports it.
434
+ * Currently only the Hailer Android app does: the document is saved to Downloads and opened
435
+ * in the system PDF viewer, where the user can print or share it. Elsewhere the document is
436
+ * only returned, as without this option; check `openedNatively` in the response.
437
+ */
438
+ openNative?: boolean;
439
+ }
431
440
  export interface PrintResponse {
432
441
  filename: string;
433
442
  /** Base64-encoded document data */
434
443
  data: string;
435
444
  mimeType: string;
445
+ /**
446
+ * Only set when `openNative` was requested: `true` if the host handed the document to the
447
+ * device's own viewer, `false` if it couldn't (web, iOS). Older Hailer versions leave it unset,
448
+ * so treat anything but `true` as "not opened".
449
+ */
450
+ openedNatively?: boolean;
436
451
  }
437
452
  export interface Settings {
438
453
  theme: 'light' | 'dark';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hailer/app-sdk",
3
- "version": "2.9.0",
3
+ "version": "2.14.0",
4
4
  "description": "SDK for building Hailer Apps",
5
5
  "author": "",
6
6
  "license": "ISC",