@acms-subir/client 0.1.8 → 0.1.10
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 +24 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +5 -1
- package/dist/types.d.ts +44 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ the cache and transport configuration with the original client.
|
|
|
49
49
|
| `unregisterDevice(token)` | Opt out (idempotent) |
|
|
50
50
|
| `pushTopics()` | Topics to offer as notification toggles |
|
|
51
51
|
| `links()` | The links page: profile, socials and visible blocks (`link`, `email`, `phone`, `contact`, `heading`, `text`, `spacer`, `live`, `embed`) |
|
|
52
|
+
| `live()` | Live video: the current broadcast (`snapshot` or `null`), `serverNow` and the owner's `display` choices. Never cached; 404 while `site.features.live` is false |
|
|
52
53
|
| `form(id)`, `submitForm(id, { answers, version?, idempotencyKey? })` | A published form's questions; send answers (checkbox groups as arrays). Each call makes one key; retries reuse it |
|
|
53
54
|
| `manageBooking(token)`, `cancelBooking(token)` | The booker's own booking from its `manageToken`, and cancelling it; `can` says what is allowed |
|
|
54
55
|
| `rescheduleAvailability(token, { from?, days?, minutes? })`, `rescheduleBooking(token, { start, minutes? })` | Open times for moving it (its own time counts as free), then the move |
|
|
@@ -140,12 +141,34 @@ await client.updateDevice(token, { topics: ['events'] });
|
|
|
140
141
|
await client.unregisterDevice(token);
|
|
141
142
|
```
|
|
142
143
|
|
|
144
|
+
## Live video
|
|
145
|
+
|
|
146
|
+
`site.features.live` is true while the site's live video (Integrations → Live
|
|
147
|
+
video) isn't off. `live()` returns the same snapshot the website shows:
|
|
148
|
+
`status` (`live`, `upcoming` or `offline` for the "Watch our channel" fallback),
|
|
149
|
+
`title`, `watchUrl`, an absolute `thumbUrl` (empty when there's none),
|
|
150
|
+
`startedAt`, `expiresAt` and optional `channel` and `viewers`. Times are Unix
|
|
151
|
+
milliseconds on the **server's** clock: keep `serverNow - Date.now()` as an
|
|
152
|
+
offset, and stop treating a snapshot as current once `expiresAt` (when nonzero)
|
|
153
|
+
passes on that clock, even if later requests fail. `display` carries the
|
|
154
|
+
owner's choices (`style`: `poster`, `split` or `row`; `bar`, `header`, `dock`)
|
|
155
|
+
so an app can follow the website. Poll about every 30 seconds while the app is
|
|
156
|
+
in the foreground; `@acms-subir/native`'s `useLive()` does all of this. The
|
|
157
|
+
"we're live" notification is the push topic `live`.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const { snapshot, serverNow } = await client.live();
|
|
161
|
+
const offset = serverNow - Date.now();
|
|
162
|
+
const current = snapshot && (!snapshot.expiresAt || snapshot.expiresAt > Date.now() + offset) ? snapshot : null;
|
|
163
|
+
if (current?.status === 'live') console.log(`Live: ${current.title} → ${current.watchUrl}`);
|
|
164
|
+
```
|
|
165
|
+
|
|
143
166
|
## Cache and headers
|
|
144
167
|
|
|
145
168
|
ETag revalidation defaults to an in-memory URL-keyed cache. Each read contacts
|
|
146
169
|
the server with `If-None-Match`; a 304 reuses the saved data. This is not an
|
|
147
170
|
offline fallback. Treat returned cached objects as immutable. Use `cache: false`
|
|
148
|
-
to disable it. Availability, booking results and errors are never cached.
|
|
171
|
+
to disable it. Availability, live status, booking results and errors are never cached.
|
|
149
172
|
Responses without an ETag or with `no-store` invalidate an older entry.
|
|
150
173
|
|
|
151
174
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Site, Theme, Page, Content, Post, ContentList, SearchResults, Links, Form, FormReceipt, FormResponseInput, ManagedBooking, RescheduleInput, ManageAvailabilityQuery, GuestCodeSent, GuestToken, GuestBookings, GuestBookingsQuery, GuestSignedOut, Events, Event, Calendars, Resources, Resource, Availability, BookingInput, BookingResult, Device, DeviceInput, DeviceUpdate, DeviceRegistration, DeviceRemoved, PushTopics } from './types.js';
|
|
1
|
+
import type { Site, Theme, Page, Content, Post, ContentList, SearchResults, Links, Form, FormReceipt, FormResponseInput, ManagedBooking, RescheduleInput, ManageAvailabilityQuery, GuestCodeSent, GuestToken, GuestBookings, GuestBookingsQuery, GuestSignedOut, Events, Event, Calendars, Resources, Resource, Availability, BookingInput, BookingResult, Device, DeviceInput, DeviceUpdate, DeviceRegistration, DeviceRemoved, PushTopics, LiveStatus } from './types.js';
|
|
2
2
|
export { AcmsError, isAcmsError } from './errors.js';
|
|
3
3
|
export type * from './types.js';
|
|
4
4
|
export * from './editor.js';
|
|
@@ -164,6 +164,10 @@ export declare const endpoints: {
|
|
|
164
164
|
readonly method: "POST";
|
|
165
165
|
readonly path: "/forms/{id}/responses";
|
|
166
166
|
};
|
|
167
|
+
readonly live: {
|
|
168
|
+
readonly method: "GET";
|
|
169
|
+
readonly path: "/live";
|
|
170
|
+
};
|
|
167
171
|
readonly manageBooking: {
|
|
168
172
|
readonly method: "GET";
|
|
169
173
|
readonly path: "/bookings/manage";
|
|
@@ -236,6 +240,12 @@ export interface AcmsClient {
|
|
|
236
240
|
pushTopics(o?: RequestOptions): Promise<PushTopics>;
|
|
237
241
|
/** The links page: profile, socials and visible blocks. Requires `site.features.links`. */
|
|
238
242
|
links(o?: RequestOptions): Promise<Links>;
|
|
243
|
+
/**
|
|
244
|
+
* The current broadcast (`snapshot`, or null), the server's clock (`serverNow`, ms) and the owner's
|
|
245
|
+
* display choices. Never cached. A live snapshot stops counting once `expiresAt` passes on the server's
|
|
246
|
+
* clock, even if later requests fail. Requires `site.features.live`.
|
|
247
|
+
*/
|
|
248
|
+
live(o?: RequestOptions): Promise<LiveStatus>;
|
|
239
249
|
/** A published form's questions. 410 when it stopped accepting responses. Requires `site.features.forms`. */
|
|
240
250
|
form(id: string, o?: RequestOptions): Promise<Form>;
|
|
241
251
|
/** Send answers (checkbox groups as arrays). Retries reuse the same Idempotency-Key, so they never duplicate. */
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,8 @@ export const endpoints = {
|
|
|
15
15
|
unregisterDevice: { method: 'DELETE', path: '/devices/{token}' }, pushTopics: { method: 'GET', path: '/push/topics' },
|
|
16
16
|
// links page and forms (owner switches in Admin → Mobile app)
|
|
17
17
|
links: { method: 'GET', path: '/links' }, form: { method: 'GET', path: '/forms/{id}' }, submitForm: { method: 'POST', path: '/forms/{id}/responses' },
|
|
18
|
+
// live video status (404 while the site's live video is off)
|
|
19
|
+
live: { method: 'GET', path: '/live' },
|
|
18
20
|
// the booker's own booking: its private token goes in the Booking-Token header
|
|
19
21
|
manageBooking: { method: 'GET', path: '/bookings/manage' }, cancelBooking: { method: 'POST', path: '/bookings/manage/cancel' },
|
|
20
22
|
rescheduleAvailability: { method: 'GET', path: '/bookings/manage/availability' }, rescheduleBooking: { method: 'POST', path: '/bookings/manage/reschedule' },
|
|
@@ -105,7 +107,8 @@ export function createClient(options) {
|
|
|
105
107
|
opts.signal?.addEventListener('abort', abort, { once: true });
|
|
106
108
|
const timer = setTimeout(() => controller.abort(new AcmsError(0, 'timeout', 'Request timed out.', undefined, undefined, key)), timeoutMs);
|
|
107
109
|
const signal = controller.signal;
|
|
108
|
-
|
|
110
|
+
// availability and live status are never served from the cache
|
|
111
|
+
const cacheable = endpoint.method === 'GET' && name !== 'availability' && name !== 'live' && !extra.headers && cache;
|
|
109
112
|
// only replay what the server treats as repeatable: a lost reply to a code
|
|
110
113
|
// exchange or a move must not run it twice
|
|
111
114
|
const replayable = endpoint.method !== 'POST' || !!key || name === 'registerDevice';
|
|
@@ -274,6 +277,7 @@ export function createClient(options) {
|
|
|
274
277
|
unregisterDevice: (token, o = {}) => request('unregisterDevice', o, {}, token),
|
|
275
278
|
pushTopics: (o = {}) => request('pushTopics', o),
|
|
276
279
|
links: (o = {}) => request('links', o),
|
|
280
|
+
live: (o = {}) => request('live', o),
|
|
277
281
|
form: (id, o = {}) => request('form', o, {}, id),
|
|
278
282
|
submitForm: (id, input, o = {}) => {
|
|
279
283
|
const { idempotencyKey, ...body } = input;
|
package/dist/types.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export type Site = {
|
|
|
25
25
|
"key": string;
|
|
26
26
|
"name": string;
|
|
27
27
|
"url": string;
|
|
28
|
+
"redirectFrom": Array<string>;
|
|
28
29
|
}>;
|
|
29
30
|
"calendar": boolean;
|
|
30
31
|
"bookings": boolean;
|
|
@@ -33,6 +34,7 @@ export type Site = {
|
|
|
33
34
|
"guestSignIn": boolean;
|
|
34
35
|
"forms": boolean;
|
|
35
36
|
"links": boolean;
|
|
37
|
+
"live": boolean;
|
|
36
38
|
};
|
|
37
39
|
"apiVersion": 1;
|
|
38
40
|
};
|
|
@@ -49,6 +51,7 @@ export type Theme = {
|
|
|
49
51
|
"accent": string;
|
|
50
52
|
"panel": string;
|
|
51
53
|
"soft": string;
|
|
54
|
+
"live": string;
|
|
52
55
|
};
|
|
53
56
|
"dark": {
|
|
54
57
|
"bg": string;
|
|
@@ -58,6 +61,7 @@ export type Theme = {
|
|
|
58
61
|
"accent": string;
|
|
59
62
|
"panel": string;
|
|
60
63
|
"soft": string;
|
|
64
|
+
"live": string;
|
|
61
65
|
};
|
|
62
66
|
};
|
|
63
67
|
"fonts": {
|
|
@@ -804,6 +808,45 @@ export type PushTopics = {
|
|
|
804
808
|
"kind": "general" | "collection" | "calendar";
|
|
805
809
|
}>;
|
|
806
810
|
};
|
|
811
|
+
export type LiveSnapshot = {
|
|
812
|
+
"status": "live" | "upcoming" | "offline" | "unknown";
|
|
813
|
+
"source": "automatic" | "manual";
|
|
814
|
+
"provider": "youtube" | "twitch" | "facebook" | "other";
|
|
815
|
+
"id": string;
|
|
816
|
+
"title": string;
|
|
817
|
+
"watchUrl": string;
|
|
818
|
+
"embedId": string;
|
|
819
|
+
"startedAt": number;
|
|
820
|
+
"checkedAt": number;
|
|
821
|
+
"expiresAt": number;
|
|
822
|
+
"channel"?: string;
|
|
823
|
+
"viewers"?: number;
|
|
824
|
+
"thumbUrl": string;
|
|
825
|
+
};
|
|
826
|
+
export type LiveStatus = {
|
|
827
|
+
"snapshot": ({
|
|
828
|
+
"status": "live" | "upcoming" | "offline" | "unknown";
|
|
829
|
+
"source": "automatic" | "manual";
|
|
830
|
+
"provider": "youtube" | "twitch" | "facebook" | "other";
|
|
831
|
+
"id": string;
|
|
832
|
+
"title": string;
|
|
833
|
+
"watchUrl": string;
|
|
834
|
+
"embedId": string;
|
|
835
|
+
"startedAt": number;
|
|
836
|
+
"checkedAt": number;
|
|
837
|
+
"expiresAt": number;
|
|
838
|
+
"channel"?: string;
|
|
839
|
+
"viewers"?: number;
|
|
840
|
+
"thumbUrl": string;
|
|
841
|
+
}) | (null);
|
|
842
|
+
"serverNow": number;
|
|
843
|
+
"display": {
|
|
844
|
+
"style": "poster" | "split" | "row";
|
|
845
|
+
"bar": boolean;
|
|
846
|
+
"header": boolean;
|
|
847
|
+
"dock": boolean;
|
|
848
|
+
};
|
|
849
|
+
};
|
|
807
850
|
export type ApiErrorResponse = {
|
|
808
851
|
"version": 1;
|
|
809
852
|
"error": {
|
|
@@ -2312,3 +2355,4 @@ export type SearchEventHit = SearchResults["events"][number];
|
|
|
2312
2355
|
export type LinkItem = Links["items"][number];
|
|
2313
2356
|
export type FormField = Form["fields"][number];
|
|
2314
2357
|
export type GuestBooking = GuestBookings["items"][number];
|
|
2358
|
+
export type LiveDisplay = LiveStatus["display"];
|