@acms-subir/client 0.1.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 +182 -0
- package/dist/editor.d.ts +291 -0
- package/dist/editor.js +354 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.js +21 -0
- package/dist/index.d.ts +171 -0
- package/dist/index.js +244 -0
- package/dist/types.d.ts +954 -0
- package/dist/types.js +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# @acms-subir/client
|
|
2
|
+
|
|
3
|
+
Dependency-free ESM TypeScript client for ACMS public API v1. Supports Expo/React
|
|
4
|
+
Native, browsers and Node 18+ using global `fetch`, `URL`, `Headers` and
|
|
5
|
+
`AbortController`. Supply `fetch` when your environment needs an adapter.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @acms-subir/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createClient, isAcmsError } from '@acms-subir/client';
|
|
13
|
+
|
|
14
|
+
const client = createClient({ baseUrl: 'https://example.com', locale: 'es' });
|
|
15
|
+
const site = await client.site();
|
|
16
|
+
const home = await client.home();
|
|
17
|
+
const page = await client.page('about/team');
|
|
18
|
+
for await (const post of client.paginate.posts({ collection: 'blog', limit: 50 })) {
|
|
19
|
+
console.log(post.title);
|
|
20
|
+
}
|
|
21
|
+
const english = client.withLocale('en');
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`baseUrl` accepts a site origin or a full `/api/v1` URL, with an optional
|
|
25
|
+
hosting prefix. All methods return **unwrapped `data`**; `openapi()` returns the
|
|
26
|
+
raw document. Every method accepts `locale` and `signal` in its options object.
|
|
27
|
+
Locale is sent only for routes that support translations. `withLocale` shares
|
|
28
|
+
the cache and transport configuration with the original client.
|
|
29
|
+
|
|
30
|
+
| Method | Options / behavior |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `site()`, `theme()`, `home()` | Identity, tokens, resolved homepage |
|
|
33
|
+
| `page(slug, opts)` | Resolved page including sections; nested slugs supported |
|
|
34
|
+
| `content(opts)` | `kind`, `collection`, `page`, `limit`, `cursor` |
|
|
35
|
+
| `contentById(id, opts)` | Optional `kind` to disambiguate imported IDs |
|
|
36
|
+
| `posts(opts)` | Content filtered to posts; `collection`, `tag`, pagination |
|
|
37
|
+
| `post(slug, opts)` | Scans post summaries, then loads detail by ID; pass `collection` for ambiguous slugs |
|
|
38
|
+
| `pages(opts)` | Page **summaries**, with pagination; use `page()` for sections |
|
|
39
|
+
| `events(opts)` | Occurrences with `from`, `to`, `calendar`, `q`, pagination |
|
|
40
|
+
| `event(slug, opts)` | First matching event from occurrences in the requested window |
|
|
41
|
+
| `calendars()` | Public calendars |
|
|
42
|
+
| `resources(opts)`, `resource(slug, opts)` | Public resources; slug lookup scans list pages |
|
|
43
|
+
| `availability(opts)` | Required `resource`, `link`; optional `from`, `days`, `minutes` |
|
|
44
|
+
| `book(opts)` | Resource booking or RSVP input, optional `idempotencyKey` |
|
|
45
|
+
| `openapi()` | Raw OpenAPI document |
|
|
46
|
+
| `registerDevice({ token, platform, locale?, topics? })` | Register or refresh an Expo push token (call on every app start); `{ created, device }` |
|
|
47
|
+
| `updateDevice(token, { topics?, locale? })` | Change a device's topics or language |
|
|
48
|
+
| `unregisterDevice(token)` | Opt out (idempotent) |
|
|
49
|
+
| `pushTopics()` | Topics to offer as notification toggles |
|
|
50
|
+
|
|
51
|
+
The server has no post-slug, event-detail or resource-detail endpoint. Those
|
|
52
|
+
convenience methods may make multiple requests. `event()` only finds events
|
|
53
|
+
with occurrences in its window (default: next 30 UTC days); an unlisted
|
|
54
|
+
calendar requires `calendar`. It does not fetch arbitrary historical events.
|
|
55
|
+
If post slugs collide and no collection is supplied, the first listed match wins.
|
|
56
|
+
|
|
57
|
+
Pagination uses `page`/`nextPage`, not opaque cursors. `cursor` is a numeric
|
|
58
|
+
alias for `page`; an explicit `page` wins. `tag` filters **each fetched page
|
|
59
|
+
locally**, so a page can be empty with a non-null `nextPage`. Iterators continue
|
|
60
|
+
through these pages. `paginate.content/posts/pages/events/resources` yield
|
|
61
|
+
individual items (events yields occurrences). They do not deduplicate changing
|
|
62
|
+
server results or provide snapshot consistency; deduplicate IDs in your app.
|
|
63
|
+
|
|
64
|
+
## Bookings, errors and cancellation
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// Persist a UUID v4 with this booking intent before submitting; reuse it until
|
|
68
|
+
// the outcome is known. Use your platform's secure UUID/storage facilities.
|
|
69
|
+
const idempotencyKey = crypto.randomUUID();
|
|
70
|
+
try {
|
|
71
|
+
const result = await client.book({
|
|
72
|
+
resource: 'hall', link: 'meeting', start: '2026-10-05T14:00:00Z', minutes: 60,
|
|
73
|
+
name: 'Example Guest', email: 'guest@example.com', idempotencyKey,
|
|
74
|
+
});
|
|
75
|
+
// Store result.manageToken securely; it grants booking-management access.
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (isAcmsError(error)) {
|
|
78
|
+
console.log(error.status, error.code, error.message, error.retryAfter);
|
|
79
|
+
// error.idempotencyKey preserves even an automatically generated key.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
const pending = client.home({ signal: controller.signal });
|
|
85
|
+
controller.abort();
|
|
86
|
+
await pending.catch(() => {});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
GET and booking POST requests retry network failures, 5xx and 429 responses at
|
|
90
|
+
most twice (three attempts total), with 250/500ms backoff and at least the
|
|
91
|
+
server's `Retry-After` delay (seconds or HTTP date). Booking retries always
|
|
92
|
+
reuse the same serialized body and UUID. By default each `book()` call creates
|
|
93
|
+
a key using `crypto.randomUUID`, falling back to a Math.random UUID v4 where
|
|
94
|
+
unavailable. For recovery across calls/app restarts, supply and persist your own
|
|
95
|
+
secure UUID. Never create a new intent merely because a response timed out.
|
|
96
|
+
|
|
97
|
+
`timeoutMs` defaults to 30,000 and covers the entire request, including retries,
|
|
98
|
+
cache access and body reading. A longer rate-limit delay may exhaust that
|
|
99
|
+
budget. Explicit aborts and timeouts are not automatically retried; an uncertain
|
|
100
|
+
booking must be retried with its existing key. `AcmsError` exposes `status`,
|
|
101
|
+
`code`, `message`, `retryAfter` (seconds), optional validation `issues` and
|
|
102
|
+
`idempotencyKey`. Local failures use status 0 and `network_error`, `timeout`,
|
|
103
|
+
`aborted`, or `invalid_response`; non-JSON HTTP errors use `http_error`.
|
|
104
|
+
Convenience lookups apply the timeout separately to each underlying request.
|
|
105
|
+
|
|
106
|
+
## Push notifications
|
|
107
|
+
|
|
108
|
+
Available when the site enables push (`acms({ mobile: { push: true } })`);
|
|
109
|
+
otherwise these return a 404 `AcmsError`. Tokens come from
|
|
110
|
+
`expo-notifications` (`getExpoPushTokenAsync`). The site stores the token,
|
|
111
|
+
platform, language and topics only.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const { topics } = await client.pushTopics(); // [{ id: 'news', label: 'News', kind: 'general' }, …]
|
|
115
|
+
await client.registerDevice({ token, platform: 'ios', locale: 'es', topics: ['news', 'events'] });
|
|
116
|
+
await client.updateDevice(token, { topics: ['events'] });
|
|
117
|
+
await client.unregisterDevice(token);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Cache and headers
|
|
121
|
+
|
|
122
|
+
ETag revalidation defaults to an in-memory URL-keyed cache. Each read contacts
|
|
123
|
+
the server with `If-None-Match`; a 304 reuses the saved data. This is not an
|
|
124
|
+
offline fallback. Treat returned cached objects as immutable. Use `cache: false`
|
|
125
|
+
to disable it. Availability, booking results and errors are never cached.
|
|
126
|
+
Responses without an ETag or with `no-store` invalidate an older entry.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import type { ClientCache } from '@acms-subir/client';
|
|
130
|
+
const cache: ClientCache = {
|
|
131
|
+
async get(url) { /* retrieve { etag, body } from app storage */ return null; },
|
|
132
|
+
async set(url, entry) { /* persist entry; null means delete */ },
|
|
133
|
+
};
|
|
134
|
+
const persistent = createClient({ baseUrl: 'https://example.com/api/v1', cache });
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Adapters may be synchronous or asynchronous. Keep persistent caches scoped to
|
|
138
|
+
one site's public API and account for storage limits. Cache adapter failures
|
|
139
|
+
propagate to the caller. Custom `headers` are supported; do not embed privileged
|
|
140
|
+
credentials. Requests omit browser credentials. Conditional headers are managed
|
|
141
|
+
by the client; a per-call booking key overrides a configured header key.
|
|
142
|
+
|
|
143
|
+
## Development
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
npm run build -w packages/client
|
|
147
|
+
npm run test -w packages/client
|
|
148
|
+
npm run check:types -w packages/client
|
|
149
|
+
npm run generate:types -w packages/client # after reviewing core schema changes
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The contract test imports the actual core OpenAPI generator using core's virtual
|
|
153
|
+
module test stubs, compares all GET/POST paths to the client method registry,
|
|
154
|
+
and checks generated request/response types byte-for-byte. HEAD and OPTIONS
|
|
155
|
+
are transport operations and have no separate data-returning methods. No core
|
|
156
|
+
code, Zod, or Node built-ins ship in this package's runtime.
|
|
157
|
+
|
|
158
|
+
## Editor apps (staff sign-in)
|
|
159
|
+
|
|
160
|
+
For sites with `acms({ mobile: { editor: { redirectUris } } })`. Staff sign in
|
|
161
|
+
in the system browser (OAuth 2.0 + PKCE); the app never sees a password.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { createEditorAuth, createEditorSession, createEditorClient } from '@acms-subir/client';
|
|
165
|
+
|
|
166
|
+
const auth = createEditorAuth({ baseUrl: 'https://example.com', clientId: 'org.example.staff', redirectUri: 'org.example.staff://auth' });
|
|
167
|
+
const session = createEditorSession({ auth, storage /* get/set backed by expo-secure-store */, onSignedOut });
|
|
168
|
+
const api = createEditorClient({ baseUrl: 'https://example.com', session });
|
|
169
|
+
|
|
170
|
+
const { url, verifier, state } = await auth.start({ deviceName: 'Ana’s iPhone' });
|
|
171
|
+
// open `url` with expo-web-browser openAuthSessionAsync, then:
|
|
172
|
+
const { code } = auth.parseRedirect(returnedUrl, state);
|
|
173
|
+
await session.signIn(await auth.exchangeCode({ code, codeVerifier: verifier }));
|
|
174
|
+
|
|
175
|
+
const doc = await api.getDocument('post', id);
|
|
176
|
+
await api.saveDocument('post', id, { revision: doc.revision, fields: { title: 'New title' } });
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
In React Native pass `crypto: { randomBytes, sha256 }` from expo-crypto to
|
|
180
|
+
`createEditorAuth` (Web Crypto is the default). Refreshes are single-flight;
|
|
181
|
+
a 401 is retried once after a refresh, then `onAuthError` runs. Writes carry
|
|
182
|
+
the `revision` you loaded (409 when someone saved first).
|
package/dist/editor.d.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { AcmsError } from './errors.js';
|
|
2
|
+
import type { EditorToken, EditorMe, EditorDocumentList, EditorDocument, EditorSections, EditorSectionResult, EditorRevision, EditorDocumentTranslation, EditorSectionTranslation, EditorMedia, EditorMediaList, EditorBooking, EditorBookingList, EditorCreateInput, EditorSaveInput, EditorListQuery, EditorSectionsQuery, EditorMediaQuery, EditorBookingsQuery } from './types.js';
|
|
3
|
+
/** Randomness + SHA-256. Defaults to Web Crypto; in React Native pass expo-crypto:
|
|
4
|
+
* { randomBytes: (n) => Crypto.getRandomBytes(n), sha256: (d) => Crypto.digest(Crypto.CryptoDigestAlgorithm.SHA256, d) } */
|
|
5
|
+
export interface PkceCrypto {
|
|
6
|
+
randomBytes(length: number): Uint8Array | Promise<Uint8Array>;
|
|
7
|
+
sha256(data: Uint8Array): ArrayBuffer | Uint8Array | Promise<ArrayBuffer | Uint8Array>;
|
|
8
|
+
}
|
|
9
|
+
export declare const webCrypto: PkceCrypto;
|
|
10
|
+
/** base64url without padding (no btoa: works in every JS runtime) */
|
|
11
|
+
export declare function base64url(bytes: Uint8Array): string;
|
|
12
|
+
/** a 43-character verifier (32 random bytes), RFC 7636 */
|
|
13
|
+
export declare function createCodeVerifier(crypto?: PkceCrypto): Promise<string>;
|
|
14
|
+
export declare function codeChallengeFor(verifier: string, crypto?: PkceCrypto): Promise<string>;
|
|
15
|
+
export declare function createPkce(crypto?: PkceCrypto): Promise<{
|
|
16
|
+
verifier: string;
|
|
17
|
+
challenge: string;
|
|
18
|
+
method: 'S256';
|
|
19
|
+
}>;
|
|
20
|
+
export declare function createState(crypto?: PkceCrypto): Promise<string>;
|
|
21
|
+
export interface EditorTokens {
|
|
22
|
+
accessToken: string;
|
|
23
|
+
refreshToken: string;
|
|
24
|
+
/** epoch ms when the access token expires */
|
|
25
|
+
expiresAt: number;
|
|
26
|
+
user: EditorToken['user'];
|
|
27
|
+
device: EditorToken['device'];
|
|
28
|
+
}
|
|
29
|
+
export declare const toTokens: (t: EditorToken, now?: number) => EditorTokens;
|
|
30
|
+
export interface EditorAuthOptions {
|
|
31
|
+
/** Site origin (or its /api/v1 base) */
|
|
32
|
+
baseUrl: string;
|
|
33
|
+
/** Your app's id, shown on the consent screen (letters, digits, . _ : -) */
|
|
34
|
+
clientId: string;
|
|
35
|
+
/** Must be listed in acms({ mobile: { editor: { redirectUris } } }) */
|
|
36
|
+
redirectUri: string;
|
|
37
|
+
fetch?: typeof globalThis.fetch;
|
|
38
|
+
crypto?: PkceCrypto;
|
|
39
|
+
timeoutMs?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface EditorAuth {
|
|
42
|
+
/** where to open the system browser (expo-web-browser openAuthSessionAsync) */
|
|
43
|
+
buildAuthorizeUrl(o: {
|
|
44
|
+
codeChallenge: string;
|
|
45
|
+
state: string;
|
|
46
|
+
deviceName?: string;
|
|
47
|
+
}): string;
|
|
48
|
+
/** PKCE + state + URL in one step; keep verifier and state until the redirect returns */
|
|
49
|
+
start(o?: {
|
|
50
|
+
deviceName?: string;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
url: string;
|
|
53
|
+
verifier: string;
|
|
54
|
+
state: string;
|
|
55
|
+
}>;
|
|
56
|
+
/** read the redirect: returns the code, throws on error, cancel or state mismatch */
|
|
57
|
+
parseRedirect(url: string, expectedState: string): {
|
|
58
|
+
code: string;
|
|
59
|
+
};
|
|
60
|
+
exchangeCode(o: {
|
|
61
|
+
code: string;
|
|
62
|
+
codeVerifier: string;
|
|
63
|
+
}): Promise<EditorTokens>;
|
|
64
|
+
/** rotates: the old refresh token stops working; reusing it signs the device out */
|
|
65
|
+
refresh(refreshToken: string): Promise<EditorTokens>;
|
|
66
|
+
revoke(token: string): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
export declare function createEditorAuth(options: EditorAuthOptions): EditorAuth;
|
|
69
|
+
export interface TokenStorage {
|
|
70
|
+
get(): EditorTokens | null | Promise<EditorTokens | null>;
|
|
71
|
+
/** null signs out */
|
|
72
|
+
set(tokens: EditorTokens | null): void | Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
export interface EditorSession {
|
|
75
|
+
getAccessToken(): Promise<string | null>;
|
|
76
|
+
/** single-flight: concurrent callers share one refresh request */
|
|
77
|
+
refresh(): Promise<string | null>;
|
|
78
|
+
signIn(tokens: EditorTokens): Promise<void>;
|
|
79
|
+
signOut(): Promise<void>;
|
|
80
|
+
tokens(): Promise<EditorTokens | null>;
|
|
81
|
+
}
|
|
82
|
+
export declare function createEditorSession(o: {
|
|
83
|
+
auth: EditorAuth;
|
|
84
|
+
storage: TokenStorage;
|
|
85
|
+
onSignedOut?: () => void;
|
|
86
|
+
refreshSkewMs?: number;
|
|
87
|
+
now?: () => number;
|
|
88
|
+
}): EditorSession;
|
|
89
|
+
export declare const authEndpoints: {
|
|
90
|
+
readonly exchangeCode: {
|
|
91
|
+
readonly method: "POST";
|
|
92
|
+
readonly path: "/auth/token";
|
|
93
|
+
};
|
|
94
|
+
readonly revoke: {
|
|
95
|
+
readonly method: "POST";
|
|
96
|
+
readonly path: "/auth/revoke";
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
export declare const editorEndpoints: {
|
|
100
|
+
readonly me: {
|
|
101
|
+
readonly method: "GET";
|
|
102
|
+
readonly path: "/admin/me";
|
|
103
|
+
};
|
|
104
|
+
readonly listDocuments: {
|
|
105
|
+
readonly method: "GET";
|
|
106
|
+
readonly path: "/admin/documents";
|
|
107
|
+
};
|
|
108
|
+
readonly createDocument: {
|
|
109
|
+
readonly method: "POST";
|
|
110
|
+
readonly path: "/admin/documents";
|
|
111
|
+
};
|
|
112
|
+
readonly getDocument: {
|
|
113
|
+
readonly method: "GET";
|
|
114
|
+
readonly path: "/admin/documents/{kind}/{id}";
|
|
115
|
+
};
|
|
116
|
+
readonly saveDocument: {
|
|
117
|
+
readonly method: "PUT";
|
|
118
|
+
readonly path: "/admin/documents/{kind}/{id}";
|
|
119
|
+
};
|
|
120
|
+
readonly publishDocument: {
|
|
121
|
+
readonly method: "POST";
|
|
122
|
+
readonly path: "/admin/documents/{kind}/{id}/publish";
|
|
123
|
+
};
|
|
124
|
+
readonly unpublishDocument: {
|
|
125
|
+
readonly method: "POST";
|
|
126
|
+
readonly path: "/admin/documents/{kind}/{id}/unpublish";
|
|
127
|
+
};
|
|
128
|
+
readonly scheduleDocument: {
|
|
129
|
+
readonly method: "POST";
|
|
130
|
+
readonly path: "/admin/documents/{kind}/{id}/schedule";
|
|
131
|
+
};
|
|
132
|
+
readonly discardDocument: {
|
|
133
|
+
readonly method: "POST";
|
|
134
|
+
readonly path: "/admin/documents/{kind}/{id}/discard";
|
|
135
|
+
};
|
|
136
|
+
readonly getDocumentTranslation: {
|
|
137
|
+
readonly method: "GET";
|
|
138
|
+
readonly path: "/admin/documents/{kind}/{id}/translations/{locale}";
|
|
139
|
+
};
|
|
140
|
+
readonly setDocumentTranslation: {
|
|
141
|
+
readonly method: "PUT";
|
|
142
|
+
readonly path: "/admin/documents/{kind}/{id}/translations/{locale}";
|
|
143
|
+
};
|
|
144
|
+
readonly reviewDocumentTranslation: {
|
|
145
|
+
readonly method: "POST";
|
|
146
|
+
readonly path: "/admin/documents/{kind}/{id}/translations/{locale}/review";
|
|
147
|
+
};
|
|
148
|
+
readonly draftDocumentTranslation: {
|
|
149
|
+
readonly method: "POST";
|
|
150
|
+
readonly path: "/admin/documents/{kind}/{id}/translations/{locale}/ai-draft";
|
|
151
|
+
};
|
|
152
|
+
readonly listSections: {
|
|
153
|
+
readonly method: "GET";
|
|
154
|
+
readonly path: "/admin/sections";
|
|
155
|
+
};
|
|
156
|
+
readonly updateSection: {
|
|
157
|
+
readonly method: "PATCH";
|
|
158
|
+
readonly path: "/admin/sections/{pageId}/{sectionId}";
|
|
159
|
+
};
|
|
160
|
+
readonly publishSection: {
|
|
161
|
+
readonly method: "POST";
|
|
162
|
+
readonly path: "/admin/sections/{pageId}/{sectionId}/publish";
|
|
163
|
+
};
|
|
164
|
+
readonly getSectionTranslation: {
|
|
165
|
+
readonly method: "GET";
|
|
166
|
+
readonly path: "/admin/sections/{pageId}/{sectionId}/translations/{locale}";
|
|
167
|
+
};
|
|
168
|
+
readonly setSectionTranslation: {
|
|
169
|
+
readonly method: "PUT";
|
|
170
|
+
readonly path: "/admin/sections/{pageId}/{sectionId}/translations/{locale}";
|
|
171
|
+
};
|
|
172
|
+
readonly reviewSectionTranslation: {
|
|
173
|
+
readonly method: "POST";
|
|
174
|
+
readonly path: "/admin/sections/{pageId}/{sectionId}/translations/{locale}/review";
|
|
175
|
+
};
|
|
176
|
+
readonly draftSectionTranslation: {
|
|
177
|
+
readonly method: "POST";
|
|
178
|
+
readonly path: "/admin/sections/{pageId}/{sectionId}/translations/{locale}/ai-draft";
|
|
179
|
+
};
|
|
180
|
+
readonly listMedia: {
|
|
181
|
+
readonly method: "GET";
|
|
182
|
+
readonly path: "/admin/media";
|
|
183
|
+
};
|
|
184
|
+
readonly uploadMedia: {
|
|
185
|
+
readonly method: "POST";
|
|
186
|
+
readonly path: "/admin/media";
|
|
187
|
+
};
|
|
188
|
+
readonly listBookings: {
|
|
189
|
+
readonly method: "GET";
|
|
190
|
+
readonly path: "/admin/bookings";
|
|
191
|
+
};
|
|
192
|
+
readonly approveBooking: {
|
|
193
|
+
readonly method: "POST";
|
|
194
|
+
readonly path: "/admin/bookings/{id}/approve";
|
|
195
|
+
};
|
|
196
|
+
readonly declineBooking: {
|
|
197
|
+
readonly method: "POST";
|
|
198
|
+
readonly path: "/admin/bookings/{id}/decline";
|
|
199
|
+
};
|
|
200
|
+
readonly cancelBooking: {
|
|
201
|
+
readonly method: "POST";
|
|
202
|
+
readonly path: "/admin/bookings/{id}/cancel";
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
export type DocKind = 'post' | 'page' | 'event';
|
|
206
|
+
/** React Native's FormData file shape, or a Blob/File on the web and Node */
|
|
207
|
+
export type UploadFile = Blob | {
|
|
208
|
+
uri: string;
|
|
209
|
+
name: string;
|
|
210
|
+
type: string;
|
|
211
|
+
};
|
|
212
|
+
export interface EditorRequestOptions {
|
|
213
|
+
signal?: AbortSignal;
|
|
214
|
+
}
|
|
215
|
+
export interface EditorClientOptions {
|
|
216
|
+
baseUrl: string;
|
|
217
|
+
/** a session from createEditorSession, or the two functions below */
|
|
218
|
+
session?: Pick<EditorSession, 'getAccessToken' | 'refresh'>;
|
|
219
|
+
getAccessToken?: () => string | null | Promise<string | null>;
|
|
220
|
+
/** return a fresh access token, or null when the person must sign in again */
|
|
221
|
+
refresh?: () => Promise<string | null>;
|
|
222
|
+
/** called when the API rejects the device (signed out, revoked, account changed) */
|
|
223
|
+
onAuthError?: (error: AcmsError) => void;
|
|
224
|
+
fetch?: typeof globalThis.fetch;
|
|
225
|
+
timeoutMs?: number;
|
|
226
|
+
}
|
|
227
|
+
export declare function createEditorClient(options: EditorClientOptions): {
|
|
228
|
+
me: (o?: EditorRequestOptions) => Promise<EditorMe>;
|
|
229
|
+
listDocuments: (q?: EditorListQuery, o?: EditorRequestOptions) => Promise<EditorDocumentList>;
|
|
230
|
+
createDocument: (input: EditorCreateInput, o?: EditorRequestOptions) => Promise<EditorDocument>;
|
|
231
|
+
getDocument: (kind: DocKind, id: string, o?: EditorRequestOptions) => Promise<EditorDocument>;
|
|
232
|
+
/** send the revision you loaded; 409 means someone saved first */
|
|
233
|
+
saveDocument: (kind: DocKind, id: string, input: EditorSaveInput, o?: EditorRequestOptions) => Promise<EditorDocument>;
|
|
234
|
+
publishDocument: (kind: DocKind, id: string, revision: string, o?: EditorRequestOptions) => Promise<EditorDocument>;
|
|
235
|
+
unpublishDocument: (kind: DocKind, id: string, revision: string, o?: EditorRequestOptions) => Promise<EditorDocument>;
|
|
236
|
+
scheduleDocument: (kind: DocKind, id: string, revision: string, at: string | Date, o?: EditorRequestOptions) => Promise<EditorDocument>;
|
|
237
|
+
discardDocument: (kind: DocKind, id: string, revision: string, o?: EditorRequestOptions) => Promise<EditorDocument>;
|
|
238
|
+
getDocumentTranslation: (kind: DocKind, id: string, locale: string, o?: EditorRequestOptions) => Promise<EditorDocumentTranslation>;
|
|
239
|
+
setDocumentTranslation: (kind: DocKind, id: string, locale: string, input: {
|
|
240
|
+
revision: string;
|
|
241
|
+
values: Record<string, string>;
|
|
242
|
+
}, o?: EditorRequestOptions) => Promise<EditorDocumentTranslation>;
|
|
243
|
+
reviewDocumentTranslation: (kind: DocKind, id: string, locale: string, input: {
|
|
244
|
+
revision: string;
|
|
245
|
+
paths?: string[];
|
|
246
|
+
}, o?: EditorRequestOptions) => Promise<EditorDocumentTranslation>;
|
|
247
|
+
draftDocumentTranslation: (kind: DocKind, id: string, locale: string, input: {
|
|
248
|
+
revision: string;
|
|
249
|
+
paths?: string[];
|
|
250
|
+
includeReviewed?: boolean;
|
|
251
|
+
}, o?: EditorRequestOptions) => Promise<EditorDocumentTranslation>;
|
|
252
|
+
listSections: (q?: EditorSectionsQuery, o?: EditorRequestOptions) => Promise<EditorSections>;
|
|
253
|
+
updateSection: (pageId: string, sectionId: string, input: {
|
|
254
|
+
revision: number;
|
|
255
|
+
data: Record<string, unknown>;
|
|
256
|
+
}, o?: EditorRequestOptions) => Promise<EditorSectionResult>;
|
|
257
|
+
publishSection: (pageId: string, sectionId: string, revision: number, o?: EditorRequestOptions) => Promise<EditorRevision>;
|
|
258
|
+
getSectionTranslation: (pageId: string, sectionId: string, locale: string, o?: EditorRequestOptions) => Promise<EditorSectionTranslation>;
|
|
259
|
+
setSectionTranslation: (pageId: string, sectionId: string, locale: string, input: {
|
|
260
|
+
revision: number;
|
|
261
|
+
values: Record<string, string>;
|
|
262
|
+
}, o?: EditorRequestOptions) => Promise<EditorSectionTranslation>;
|
|
263
|
+
reviewSectionTranslation: (pageId: string, sectionId: string, locale: string, input: {
|
|
264
|
+
revision: number;
|
|
265
|
+
paths?: string[];
|
|
266
|
+
}, o?: EditorRequestOptions) => Promise<EditorSectionTranslation>;
|
|
267
|
+
draftSectionTranslation: (pageId: string, sectionId: string, locale: string, input: {
|
|
268
|
+
revision: number;
|
|
269
|
+
paths?: string[];
|
|
270
|
+
includeReviewed?: boolean;
|
|
271
|
+
}, o?: EditorRequestOptions) => Promise<EditorSectionTranslation>;
|
|
272
|
+
listMedia: (q?: EditorMediaQuery, o?: EditorRequestOptions) => Promise<EditorMediaList>;
|
|
273
|
+
/** compress images on the device first (same limits as the admin: 30 MB images, 25 MB documents) */
|
|
274
|
+
uploadMedia: (file: UploadFile, meta?: {
|
|
275
|
+
name?: string;
|
|
276
|
+
alt?: string;
|
|
277
|
+
width?: number;
|
|
278
|
+
height?: number;
|
|
279
|
+
}, o?: EditorRequestOptions) => Promise<EditorMedia>;
|
|
280
|
+
listBookings: (q?: EditorBookingsQuery, o?: EditorRequestOptions) => Promise<EditorBookingList>;
|
|
281
|
+
approveBooking: (id: string, input?: {
|
|
282
|
+
message?: string;
|
|
283
|
+
}, o?: EditorRequestOptions) => Promise<EditorBooking>;
|
|
284
|
+
declineBooking: (id: string, input?: {
|
|
285
|
+
message?: string;
|
|
286
|
+
}, o?: EditorRequestOptions) => Promise<EditorBooking>;
|
|
287
|
+
cancelBooking: (id: string, input?: {
|
|
288
|
+
message?: string;
|
|
289
|
+
}, o?: EditorRequestOptions) => Promise<EditorBooking>;
|
|
290
|
+
};
|
|
291
|
+
export type EditorClient = ReturnType<typeof createEditorClient>;
|