lauther-client 0.3.0 → 0.4.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 CHANGED
@@ -84,7 +84,8 @@ const { response } = await lauther.approve({
84
84
 
85
85
  A request is an approval whose "yes" carries something back: the person taps
86
86
  **Share**, the phone does one thing while they watch, and the result rides
87
- home on the answer. Two kinds: where they are, and one picture.
87
+ home on the answer. Three kinds: where they are, one picture, and a short
88
+ typed answer.
88
89
 
89
90
  ```js
90
91
  const where = await lauther.requestLocation({
@@ -105,7 +106,18 @@ if (shot.response === "approve") {
105
106
  }
106
107
  ```
107
108
 
108
- Both return `{ id, response, confirmedBy, result, waitedSeconds, expired }`.
109
+ ```js
110
+ const code = await lauther.requestText({
111
+ title: "What is the 6-digit code from your authenticator?",
112
+ placeholder: "6-digit code",
113
+ });
114
+
115
+ if (code.response === "approve") {
116
+ const { text } = code.result; // what they typed
117
+ }
118
+ ```
119
+
120
+ All three return `{ id, response, confirmedBy, result, waitedSeconds, expired }`.
109
121
  `response` is `"approve"`, `"deny"` or `null`, and `result` is `null` unless
110
122
  the person approved. They wait the way `requestApproval()` does: past the
111
123
  server's 25 seconds on the **same** request, so the phone still shows one.
@@ -117,9 +129,14 @@ means the request lapsed on the phone (10 minutes by default; set
117
129
  `{ lat, lng, accuracy, at }`, with `accuracy` in metres.
118
130
  - **Photo** is a JPEG they take or pick, at most 2 MB. You receive a link,
119
131
  and **the link works for 7 days**; download it if you want to keep it.
120
-
121
- Options for both: `title`, `message`, `requireBiometric`, `priority`,
122
- `expiresIn`, plus `timeoutSeconds` and `pollIntervalMs` for the wait. For
132
+ - **Text** is a short answer they type on the phone: `{ text }`, 1 to 500
133
+ characters. Use it for a code, a choice, a short fact only they know. The
134
+ optional `placeholder` (up to 80 characters) hints at the shape of the
135
+ answer, such as "6-digit code".
136
+
137
+ Options for all three: `title`, `message`, `requireBiometric`, `priority`,
138
+ `expiresIn`, plus `timeoutSeconds` and `pollIntervalMs` for the wait;
139
+ `requestText()` also takes `placeholder`. For
123
140
  full control, `request(kind, options)` is the single call behind them (it
124
141
  answers within `wait` seconds, 0 to 25), and `poll(id)` also returns `result`
125
142
  and `expired`.
@@ -163,9 +180,10 @@ reported separately rather than collapsed into one flag.
163
180
  | `push(options)` | `title`, `message`, `priority`, `url`, `path`, `tag`, `icon`, `color`, `sound`, `image` |
164
181
  | `approve(options)` | `title`, `message`, `wait` (0–25s), `approveTitle`, `denyTitle`, `requireBiometric`, `priority` |
165
182
  | `requestApproval(question, options?)` | Ask and wait, past the 25s cap, on one notification |
166
- | `request(kind, options?)` | Ask for `"location"` or `"photo"`; `title`, `message`, `requireBiometric`, `priority`, `wait`, `expiresIn` |
183
+ | `request(kind, options?)` | Ask for `"location"`, `"photo"` or `"text"`; `title`, `message`, `requireBiometric`, `priority`, `wait`, `expiresIn`, `placeholder` |
167
184
  | `requestLocation(options?)` | Ask for a position and wait, past the 25s cap, on one request |
168
185
  | `requestPhoto(options?)` | Ask for one picture and wait, past the 25s cap |
186
+ | `requestText(options?)` | Ask a short question and wait for the typed answer, past the 25s cap |
169
187
  | `poll(id)` | Ask about an approval or request already started (carries `result` and `expired`) |
170
188
  | `ask(question, options?)` | `requestApproval()` reduced to a boolean |
171
189
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lauther-client",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Send push notifications to your phone from any web app or script — and ask questions that wait for a yes.",
5
5
  "keywords": [
6
6
  "push",
package/src/index.d.ts CHANGED
@@ -70,10 +70,10 @@ export interface PollResult {
70
70
  /** How the answer was confirmed, e.g. a biometric check. `null` if not. */
71
71
  confirmedBy: string | null;
72
72
  /**
73
- * What a request carried back: a `LocationResult` or `PhotoResult` once
74
- * approved, `null` until then, when denied, and for plain approvals.
73
+ * What a request carried back: a `LocationResult`, `PhotoResult` or
74
+ * `TextResult` once approved, `null` until then, when denied, and for plain approvals.
75
75
  */
76
- result?: LocationResult | PhotoResult | null;
76
+ result?: LocationResult | PhotoResult | TextResult | null;
77
77
  /** `true` once a request has lapsed unanswered; there is no point polling on. */
78
78
  expired?: boolean;
79
79
  }
@@ -98,10 +98,13 @@ export interface ApprovalOutcome {
98
98
  }
99
99
 
100
100
  /** The kinds of thing a person can be asked to share. */
101
- export type RequestKind = "location" | "photo";
101
+ export type RequestKind = "location" | "photo" | "text";
102
102
 
103
103
  export interface RequestOptions {
104
- /** The question. Defaults to "Share your location?" / "Take a photo?". */
104
+ /**
105
+ * The question. Defaults to "Share your location?" / "Take a photo?" /
106
+ * "Answer a question?".
107
+ */
105
108
  title?: string;
106
109
  message?: string;
107
110
  /** Demand a fingerprint or face before the share goes out. */
@@ -111,6 +114,11 @@ export interface RequestOptions {
111
114
  wait?: number;
112
115
  /** Seconds the request stays answerable, 60-3600. Defaults to 600. */
113
116
  expiresIn?: number;
117
+ /**
118
+ * Text requests only: a hint at the shape of the answer, shown in the
119
+ * field on the phone ("6-digit code"). Up to 80 characters.
120
+ */
121
+ placeholder?: string;
114
122
  }
115
123
 
116
124
  /** One position, as the phone read it a moment before sending. */
@@ -132,13 +140,18 @@ export interface PhotoResult {
132
140
  height?: number;
133
141
  }
134
142
 
143
+ /** A typed answer: 1 to 500 characters, trimmed. */
144
+ export interface TextResult {
145
+ text: string;
146
+ }
147
+
135
148
  export interface RequestResult {
136
149
  ok: true;
137
150
  id: string;
138
151
  /** `null` means nobody answered in time. Silence is not consent. */
139
152
  response: "approve" | "deny" | null;
140
153
  /** What was shared. `null` unless the person approved. */
141
- result: LocationResult | PhotoResult | null;
154
+ result: LocationResult | PhotoResult | TextResult | null;
142
155
  }
143
156
 
144
157
  export interface RequestWaitOptions extends Omit<RequestOptions, "wait"> {
@@ -152,7 +165,7 @@ export interface RequestWaitOptions extends Omit<RequestOptions, "wait"> {
152
165
  pollIntervalMs?: number;
153
166
  }
154
167
 
155
- export interface RequestOutcome<R = LocationResult | PhotoResult> {
168
+ export interface RequestOutcome<R = LocationResult | PhotoResult | TextResult> {
156
169
  id: string;
157
170
  /** `null` means nobody answered, or the request expired. */
158
171
  response: "approve" | "deny" | null;
@@ -191,12 +204,14 @@ export declare class Lauther {
191
204
  poll(id: string): Promise<PollResult>;
192
205
  /** Ask and wait, past the server's 25s cap, on one notification. */
193
206
  requestApproval(question: string, options?: WaitOptions): Promise<ApprovalOutcome>;
194
- /** Ask the person to share `"location"` or a `"photo"`; answers within `wait` seconds. */
207
+ /** Ask the person to share `"location"`, a `"photo"` or a typed `"text"`; answers within `wait` seconds. */
195
208
  request(kind: RequestKind, options?: RequestOptions): Promise<RequestResult>;
196
209
  /** Ask for the person's position and wait, past the server's 25s cap. */
197
210
  requestLocation(options?: RequestWaitOptions): Promise<RequestOutcome<LocationResult>>;
198
211
  /** Ask for one picture and wait, past the server's 25s cap. */
199
212
  requestPhoto(options?: RequestWaitOptions): Promise<RequestOutcome<PhotoResult>>;
213
+ /** Ask a short question and wait for the typed answer, past the server's 25s cap. */
214
+ requestText(options?: RequestWaitOptions): Promise<RequestOutcome<TextResult>>;
200
215
  /** True only on an explicit Approve; a denial and a timeout are both false. */
201
216
  ask(question: string, options?: WaitOptions): Promise<boolean>;
202
217
  }
package/src/index.js CHANGED
@@ -180,21 +180,21 @@ export class Lauther {
180
180
  }
181
181
 
182
182
  /**
183
- * Ask the person to share something from their phone: where they are, or a
184
- * picture. The "yes" carries the payload.
183
+ * Ask the person to share something from their phone: where they are, a
184
+ * picture, or a short typed answer. The "yes" carries the payload.
185
185
  *
186
186
  * Like `approve()` this blocks server-side for up to 25 seconds, and
187
187
  * `response` is `"approve"`, `"deny"` or `null`. On an approve, `result`
188
- * holds what was shared (see `LocationResult` and `PhotoResult`); on
189
- * anything else it is `null`.
188
+ * holds what was shared (see `LocationResult`, `PhotoResult` and
189
+ * `TextResult`); on anything else it is `null`.
190
190
  *
191
- * @param {"location" | "photo"} kind
191
+ * @param {"location" | "photo" | "text"} kind
192
192
  * @param {import("./index.d.ts").RequestOptions} [options]
193
193
  * @returns {Promise<import("./index.d.ts").RequestResult>}
194
194
  */
195
195
  async request(kind, options) {
196
196
  if (!kind) {
197
- throw new LautherError('request() needs a kind: "location" or "photo".');
197
+ throw new LautherError('request() needs a kind: "location", "photo" or "text".');
198
198
  }
199
199
  const {wait = MAX_WAIT_SECONDS, ...rest} = options ?? {};
200
200
  return this.#request("/v1/request", {
@@ -229,11 +229,24 @@ export class Lauther {
229
229
  }
230
230
 
231
231
  /**
232
- * Shared by `requestLocation()` and `requestPhoto()`: start the request,
233
- * then keep asking about the SAME notification until it is answered,
234
- * expires, or `timeoutSeconds` runs out.
232
+ * Ask a short question and wait for the typed answer: a code, a name, a
233
+ * choice. Same waiting rules as `requestApproval()`. `options.placeholder`
234
+ * (up to 80 characters) hints at the shape of the answer on the phone, for
235
+ * example "6-digit code". `result.text` is 1 to 500 characters.
235
236
  *
236
- * @param {"location" | "photo"} kind
237
+ * @param {import("./index.d.ts").RequestWaitOptions} [options]
238
+ * @returns {Promise<import("./index.d.ts").RequestOutcome<import("./index.d.ts").TextResult>>}
239
+ */
240
+ requestText(options = {}) {
241
+ return this.#requestAndWait("text", options);
242
+ }
243
+
244
+ /**
245
+ * Shared by `requestLocation()`, `requestPhoto()` and `requestText()`:
246
+ * start the request, then keep asking about the SAME notification until it
247
+ * is answered, expires, or `timeoutSeconds` runs out.
248
+ *
249
+ * @param {"location" | "photo" | "text"} kind
237
250
  * @param {import("./index.d.ts").RequestWaitOptions} options
238
251
  */
239
252
  async #requestAndWait(kind, options) {