@mx-space/byline-api-client 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +200 -2
- package/dist/index.cjs +573 -0
- package/dist/index.d.cts +653 -0
- package/dist/index.d.ts +653 -0
- package/dist/index.js +566 -0
- package/package.json +54 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
//#region src/case.ts
|
|
2
|
+
const isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
|
|
3
|
+
const camelcase = (key) => key.replaceAll(/_([\da-z])/gi, (_, char) => char.toUpperCase());
|
|
4
|
+
const splitPath = (path) => path.replaceAll("[]", ".[]").split(".").filter((segment) => segment.length > 0);
|
|
5
|
+
const buildTree = (paths) => {
|
|
6
|
+
const root = /* @__PURE__ */ new Map();
|
|
7
|
+
for (const path of paths) {
|
|
8
|
+
const segments = splitPath(path);
|
|
9
|
+
let node = root;
|
|
10
|
+
segments.forEach((segment, index) => {
|
|
11
|
+
if (index === segments.length - 1) {
|
|
12
|
+
node.set(segment, true);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const next = node.get(segment);
|
|
16
|
+
if (next === true) return;
|
|
17
|
+
const child = next ?? /* @__PURE__ */ new Map();
|
|
18
|
+
node.set(segment, child);
|
|
19
|
+
node = child;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return root;
|
|
23
|
+
};
|
|
24
|
+
const walk = (value, tree) => {
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
const element = tree?.get("[]");
|
|
27
|
+
if (element === true) return value;
|
|
28
|
+
return value.map((item) => walk(item, element));
|
|
29
|
+
}
|
|
30
|
+
if (!isPlainObject(value)) return value;
|
|
31
|
+
const out = {};
|
|
32
|
+
for (const [key, item] of Object.entries(value)) {
|
|
33
|
+
const nextKey = camelcase(key);
|
|
34
|
+
const branch = tree?.get(nextKey);
|
|
35
|
+
out[nextKey] = branch === true ? item : walk(item, branch);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
};
|
|
39
|
+
const camelcaseKeys = (value, preservePaths = []) => walk(value, buildTree(preservePaths));
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/error.ts
|
|
42
|
+
var BylineApiError = class extends Error {
|
|
43
|
+
status;
|
|
44
|
+
code;
|
|
45
|
+
path;
|
|
46
|
+
details;
|
|
47
|
+
name = "BylineApiError";
|
|
48
|
+
constructor(status, code, message, path, details) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.status = status;
|
|
51
|
+
this.code = code;
|
|
52
|
+
this.path = path;
|
|
53
|
+
this.details = details;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const isBylineApiError = (error, code) => error instanceof BylineApiError && (code === void 0 || error.code === code);
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/http.ts
|
|
59
|
+
const ENVELOPE_KEYS = /* @__PURE__ */ new Set(["data", "meta"]);
|
|
60
|
+
const isEnvelope = (body) => !!body && typeof body === "object" && !Array.isArray(body) && "data" in body && Object.keys(body).every((key) => ENVELOPE_KEYS.has(key));
|
|
61
|
+
const isBinaryBody = (body) => typeof FormData !== "undefined" && body instanceof FormData || typeof Blob !== "undefined" && body instanceof Blob || body instanceof ArrayBuffer || body instanceof URLSearchParams || typeof body === "string";
|
|
62
|
+
var HttpCore = class {
|
|
63
|
+
options;
|
|
64
|
+
endpoint;
|
|
65
|
+
fetchImpl;
|
|
66
|
+
constructor(options) {
|
|
67
|
+
this.options = options;
|
|
68
|
+
if (!options.endpoint) throw new TypeError("endpoint is required");
|
|
69
|
+
this.endpoint = options.endpoint.replace(/\/+$/, "");
|
|
70
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
71
|
+
if (!fetchImpl) throw new TypeError("No fetch implementation available");
|
|
72
|
+
this.fetchImpl = fetchImpl;
|
|
73
|
+
}
|
|
74
|
+
get lang() {
|
|
75
|
+
return this.options.lang;
|
|
76
|
+
}
|
|
77
|
+
buildUrl(path, query) {
|
|
78
|
+
const url = new URL(`${this.endpoint}/${path.replace(/^\/+/, "")}`);
|
|
79
|
+
for (const [key, value] of Object.entries(query ?? {})) {
|
|
80
|
+
if (value === void 0 || value === null) continue;
|
|
81
|
+
url.searchParams.set(key, Array.isArray(value) ? value.join(",") : String(value));
|
|
82
|
+
}
|
|
83
|
+
return url.toString();
|
|
84
|
+
}
|
|
85
|
+
async request(method, path, options = {}) {
|
|
86
|
+
const headers = {
|
|
87
|
+
accept: "application/json",
|
|
88
|
+
...this.options.headers,
|
|
89
|
+
...options.headers
|
|
90
|
+
};
|
|
91
|
+
if (this.options.apiKey) headers["x-api-key"] = this.options.apiKey;
|
|
92
|
+
if (this.options.token) headers.authorization = `Bearer ${this.options.token}`;
|
|
93
|
+
let body;
|
|
94
|
+
if (options.body !== void 0) {
|
|
95
|
+
if (isBinaryBody(options.body)) body = options.body;
|
|
96
|
+
else {
|
|
97
|
+
headers["content-type"] = "application/json";
|
|
98
|
+
body = JSON.stringify(options.body);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const url = this.buildUrl(path, options.query);
|
|
102
|
+
const response = await this.fetchImpl(url, {
|
|
103
|
+
method,
|
|
104
|
+
headers,
|
|
105
|
+
body,
|
|
106
|
+
signal: options.signal
|
|
107
|
+
});
|
|
108
|
+
if (response.status === 204) return { data: void 0 };
|
|
109
|
+
const payload = (response.headers.get("content-type") ?? "").includes("json") ? await response.json().catch(() => null) : await response.text();
|
|
110
|
+
if (!response.ok) throw this.toError(response.status, path, payload);
|
|
111
|
+
if (!isEnvelope(payload)) return { data: this.transform(payload, options.preserve) };
|
|
112
|
+
return {
|
|
113
|
+
data: this.transform(payload.data, options.preserve),
|
|
114
|
+
...payload.meta === void 0 ? {} : { meta: this.transform(payload.meta) }
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
transform(value, preserve) {
|
|
118
|
+
if (this.options.camelCase === false) return value;
|
|
119
|
+
return camelcaseKeys(value, preserve);
|
|
120
|
+
}
|
|
121
|
+
toError(status, path, payload) {
|
|
122
|
+
const error = payload && typeof payload === "object" && "error" in payload ? payload.error : void 0;
|
|
123
|
+
if (error && typeof error === "object") return new BylineApiError(status, String(error.code ?? "HTTP_ERROR"), String(error.message ?? `Request failed with status ${status}`), path, error.details === void 0 ? void 0 : camelcaseKeys(error.details));
|
|
124
|
+
return new BylineApiError(status, "HTTP_ERROR", typeof payload === "string" && payload ? payload.slice(0, 500) : `Request failed with status ${status}`, path);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/resources/authors.ts
|
|
129
|
+
var AuthorsResource = class {
|
|
130
|
+
http;
|
|
131
|
+
constructor(http) {
|
|
132
|
+
this.http = http;
|
|
133
|
+
}
|
|
134
|
+
list(query = {}) {
|
|
135
|
+
return this.http.request("GET", "/authors", { query: { ...query } });
|
|
136
|
+
}
|
|
137
|
+
async listAll() {
|
|
138
|
+
const authors = [];
|
|
139
|
+
for (let page = 1;; page++) {
|
|
140
|
+
const { data, meta } = await this.list({
|
|
141
|
+
page,
|
|
142
|
+
size: 100
|
|
143
|
+
});
|
|
144
|
+
authors.push(...data);
|
|
145
|
+
if (!meta || page >= meta.pagination.totalPages || !data.length) break;
|
|
146
|
+
}
|
|
147
|
+
return authors;
|
|
148
|
+
}
|
|
149
|
+
get(idOrUsername) {
|
|
150
|
+
return this.http.request("GET", `/authors/${encodeURIComponent(idOrUsername)}`);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/resources/content.ts
|
|
155
|
+
const CATALOG_PRESERVE = ["components[].propsSchema"];
|
|
156
|
+
var CategoriesResource = class {
|
|
157
|
+
http;
|
|
158
|
+
constructor(http) {
|
|
159
|
+
this.http = http;
|
|
160
|
+
}
|
|
161
|
+
list(options = {}) {
|
|
162
|
+
return this.http.request("GET", "/categories", { query: { lang: options.lang ?? this.http.lang } });
|
|
163
|
+
}
|
|
164
|
+
get(slugOrId, options = {}) {
|
|
165
|
+
return this.http.request("GET", `/categories/${encodeURIComponent(slugOrId)}`, { query: { lang: options.lang ?? this.http.lang } });
|
|
166
|
+
}
|
|
167
|
+
create(input) {
|
|
168
|
+
return this.http.request("POST", "/categories", { body: input });
|
|
169
|
+
}
|
|
170
|
+
update(id, input) {
|
|
171
|
+
return this.http.request("PATCH", `/categories/${id}`, { body: input });
|
|
172
|
+
}
|
|
173
|
+
delete(id) {
|
|
174
|
+
return this.http.request("DELETE", `/categories/${id}`);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var TranslationsResource = class {
|
|
178
|
+
http;
|
|
179
|
+
constructor(http) {
|
|
180
|
+
this.http = http;
|
|
181
|
+
}
|
|
182
|
+
getForArticle(articleId, lang) {
|
|
183
|
+
return this.http.request("GET", `/ai/translations/article/${articleId}`, { query: { lang } });
|
|
184
|
+
}
|
|
185
|
+
languages(articleId) {
|
|
186
|
+
return this.http.request("GET", `/ai/translations/article/${articleId}/languages`);
|
|
187
|
+
}
|
|
188
|
+
listByRef(articleId) {
|
|
189
|
+
return this.http.request("GET", `/ai/translations/ref/${articleId}`, { preserve: ["article.document.meta"] });
|
|
190
|
+
}
|
|
191
|
+
writeManual(articleId, lang, input) {
|
|
192
|
+
return this.http.request("PUT", `/ai/translations/article/${articleId}/${encodeURIComponent(lang)}`, { body: {
|
|
193
|
+
...input,
|
|
194
|
+
content: typeof input.content === "string" ? input.content : JSON.stringify(input.content)
|
|
195
|
+
} });
|
|
196
|
+
}
|
|
197
|
+
update(id, patch) {
|
|
198
|
+
return this.http.request("PATCH", `/ai/translations/${id}`, { body: patch });
|
|
199
|
+
}
|
|
200
|
+
delete(id) {
|
|
201
|
+
return this.http.request("DELETE", `/ai/translations/${id}`);
|
|
202
|
+
}
|
|
203
|
+
createTask(input) {
|
|
204
|
+
return this.http.request("POST", "/ai/translations/task", { body: input });
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
var DynamicComponentsResource = class {
|
|
208
|
+
http;
|
|
209
|
+
constructor(http) {
|
|
210
|
+
this.http = http;
|
|
211
|
+
}
|
|
212
|
+
list() {
|
|
213
|
+
return this.http.request("GET", "/dynamic-components", { preserve: CATALOG_PRESERVE });
|
|
214
|
+
}
|
|
215
|
+
register(input) {
|
|
216
|
+
return this.http.request("POST", "/dynamic-components", {
|
|
217
|
+
body: input,
|
|
218
|
+
preserve: CATALOG_PRESERVE
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
var FilesResource = class {
|
|
223
|
+
http;
|
|
224
|
+
constructor(http) {
|
|
225
|
+
this.http = http;
|
|
226
|
+
}
|
|
227
|
+
upload(file, options = {}) {
|
|
228
|
+
const form = new FormData();
|
|
229
|
+
const filename = options.filename ?? (typeof File !== "undefined" && file instanceof File ? file.name : "upload.bin");
|
|
230
|
+
form.append("file", file, filename);
|
|
231
|
+
return this.http.request("POST", "/files/upload", {
|
|
232
|
+
query: {
|
|
233
|
+
type: options.type,
|
|
234
|
+
immutable: options.immutable ? "true" : void 0
|
|
235
|
+
},
|
|
236
|
+
body: form
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/resources/drafts.ts
|
|
242
|
+
var DraftsResource = class {
|
|
243
|
+
http;
|
|
244
|
+
constructor(http) {
|
|
245
|
+
this.http = http;
|
|
246
|
+
}
|
|
247
|
+
list(query = {}) {
|
|
248
|
+
return this.http.request("GET", "/drafts", { query });
|
|
249
|
+
}
|
|
250
|
+
get(id) {
|
|
251
|
+
return this.http.request("GET", `/drafts/${id}`);
|
|
252
|
+
}
|
|
253
|
+
context(refType, refId) {
|
|
254
|
+
return this.http.request("GET", `/drafts/context/${refType}/${refId}`);
|
|
255
|
+
}
|
|
256
|
+
create(input) {
|
|
257
|
+
return this.http.request("POST", "/drafts", { body: input });
|
|
258
|
+
}
|
|
259
|
+
update(id, input) {
|
|
260
|
+
return this.http.request("PUT", `/drafts/${id}`, { body: input });
|
|
261
|
+
}
|
|
262
|
+
delete(id) {
|
|
263
|
+
return this.http.request("DELETE", `/drafts/${id}`);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/resources/posts.ts
|
|
268
|
+
const POST_PRESERVE = ["meta"];
|
|
269
|
+
const POST_LIST_PRESERVE = ["[].meta"];
|
|
270
|
+
const DRAFT_CORE_KEYS = /* @__PURE__ */ new Set([
|
|
271
|
+
"title",
|
|
272
|
+
"text",
|
|
273
|
+
"content",
|
|
274
|
+
"images",
|
|
275
|
+
"meta"
|
|
276
|
+
]);
|
|
277
|
+
const toDraftData = (input) => {
|
|
278
|
+
const data = { contentFormat: "lexical" };
|
|
279
|
+
const typeSpecificData = {};
|
|
280
|
+
for (const [key, value] of Object.entries(input)) {
|
|
281
|
+
if (value === void 0) continue;
|
|
282
|
+
if (key === "content") data.content = typeof value === "string" ? value : JSON.stringify(value);
|
|
283
|
+
else if (DRAFT_CORE_KEYS.has(key)) data[key] = value;
|
|
284
|
+
else typeSpecificData[key] = value;
|
|
285
|
+
}
|
|
286
|
+
if (data.content !== void 0 && data.text === void 0) data.text = "";
|
|
287
|
+
if (Object.keys(typeSpecificData).length > 0) data.typeSpecificData = typeSpecificData;
|
|
288
|
+
return data;
|
|
289
|
+
};
|
|
290
|
+
const mergeSnapshot = (base, patch) => ({
|
|
291
|
+
title: base.title,
|
|
292
|
+
text: base.text,
|
|
293
|
+
content: base.content ?? void 0,
|
|
294
|
+
images: base.images,
|
|
295
|
+
meta: base.meta,
|
|
296
|
+
...patch,
|
|
297
|
+
contentFormat: "lexical",
|
|
298
|
+
typeSpecificData: {
|
|
299
|
+
...base.typeSpecificData,
|
|
300
|
+
...patch.typeSpecificData
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
var PostsResource = class {
|
|
304
|
+
http;
|
|
305
|
+
drafts;
|
|
306
|
+
publishJobs;
|
|
307
|
+
constructor(http, drafts, publishJobs) {
|
|
308
|
+
this.http = http;
|
|
309
|
+
this.drafts = drafts;
|
|
310
|
+
this.publishJobs = publishJobs;
|
|
311
|
+
}
|
|
312
|
+
list(query = {}) {
|
|
313
|
+
return this.http.request("GET", "/posts", {
|
|
314
|
+
query: {
|
|
315
|
+
...query,
|
|
316
|
+
lang: query.lang ?? this.http.lang
|
|
317
|
+
},
|
|
318
|
+
preserve: POST_LIST_PRESERVE
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
get(id, options = {}) {
|
|
322
|
+
return this.http.request("GET", `/posts/${id}`, {
|
|
323
|
+
query: { lang: options.lang ?? this.http.lang },
|
|
324
|
+
preserve: POST_PRESERVE
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
getBySlug(categorySlug, slug, options = {}) {
|
|
328
|
+
return this.http.request("GET", `/posts/${encodeURIComponent(categorySlug)}/${encodeURIComponent(slug)}`, {
|
|
329
|
+
query: { lang: options.lang ?? this.http.lang },
|
|
330
|
+
preserve: POST_PRESERVE
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
getPath(slug) {
|
|
334
|
+
return this.http.request("GET", `/posts/get-url/${encodeURIComponent(slug)}`);
|
|
335
|
+
}
|
|
336
|
+
async findBySlug(slug, options = {}) {
|
|
337
|
+
try {
|
|
338
|
+
const { data } = await this.getPath(slug);
|
|
339
|
+
const [category, postSlug] = data.path.replace(/^\/+/, "").split("/");
|
|
340
|
+
if (!category || !postSlug) return null;
|
|
341
|
+
return await this.getBySlug(category, decodeURIComponent(postSlug), options);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
if (isBylineApiError(error) && error.status === 404) return null;
|
|
344
|
+
throw error;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
latest(options = {}) {
|
|
348
|
+
return this.http.request("GET", "/posts/latest", {
|
|
349
|
+
query: { lang: options.lang ?? this.http.lang },
|
|
350
|
+
preserve: POST_PRESERVE
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
update(id, patch) {
|
|
354
|
+
return this.http.request("PATCH", `/posts/${id}`, { body: patch });
|
|
355
|
+
}
|
|
356
|
+
setPublished(id, isPublished) {
|
|
357
|
+
return this.http.request("PATCH", `/posts/${id}/publish`, { body: { isPublished } });
|
|
358
|
+
}
|
|
359
|
+
delete(id) {
|
|
360
|
+
return this.http.request("DELETE", `/posts/${id}`);
|
|
361
|
+
}
|
|
362
|
+
async create(input, options = {}) {
|
|
363
|
+
const { data: draft } = await this.drafts.create({
|
|
364
|
+
refType: "post",
|
|
365
|
+
data: toDraftData(input)
|
|
366
|
+
});
|
|
367
|
+
return this.finish(draft, options);
|
|
368
|
+
}
|
|
369
|
+
async revise(id, input, options = {}) {
|
|
370
|
+
const { data: context } = await this.drafts.context("post", id);
|
|
371
|
+
const base = context.publishedRevision;
|
|
372
|
+
if (!base) throw new BylineApiError(404, "CONTENT_REVISION_NOT_FOUND", `Post ${id} has no published revision to branch from`, `/drafts/context/post/${id}`);
|
|
373
|
+
const { data: draft } = await this.drafts.create({
|
|
374
|
+
refType: "post",
|
|
375
|
+
refId: id,
|
|
376
|
+
baseRevisionId: base.id,
|
|
377
|
+
data: mergeSnapshot(base, toDraftData(input))
|
|
378
|
+
});
|
|
379
|
+
return this.finish(draft, options);
|
|
380
|
+
}
|
|
381
|
+
async finish(draft, options) {
|
|
382
|
+
if (options.publish === false) return {
|
|
383
|
+
draft,
|
|
384
|
+
postId: draft.document.refId,
|
|
385
|
+
taskId: null
|
|
386
|
+
};
|
|
387
|
+
const task = await this.publishJobs.run({
|
|
388
|
+
branchId: draft.id,
|
|
389
|
+
revisionId: draft.headRevisionId,
|
|
390
|
+
expectedPublishedRevisionId: draft.document.publishedRevisionId,
|
|
391
|
+
confirmDiverged: draft.relationToPublished === "descendant" || draft.relationToPublished === "diverged",
|
|
392
|
+
aiResources: options.aiResources,
|
|
393
|
+
regenerateLockedTranslations: options.regenerateLockedTranslations
|
|
394
|
+
}, options);
|
|
395
|
+
return {
|
|
396
|
+
draft,
|
|
397
|
+
postId: task.result.articleId,
|
|
398
|
+
taskId: task.id
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
//#endregion
|
|
403
|
+
//#region src/resources/tasks.ts
|
|
404
|
+
const TERMINAL = /* @__PURE__ */ new Set([
|
|
405
|
+
"completed",
|
|
406
|
+
"partial_failed",
|
|
407
|
+
"failed",
|
|
408
|
+
"cancelled"
|
|
409
|
+
]);
|
|
410
|
+
const sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
411
|
+
const timer = setTimeout(resolve, ms);
|
|
412
|
+
signal?.addEventListener("abort", () => {
|
|
413
|
+
clearTimeout(timer);
|
|
414
|
+
reject(signal.reason);
|
|
415
|
+
}, { once: true });
|
|
416
|
+
});
|
|
417
|
+
var TasksResource = class {
|
|
418
|
+
http;
|
|
419
|
+
constructor(http) {
|
|
420
|
+
this.http = http;
|
|
421
|
+
}
|
|
422
|
+
get(id) {
|
|
423
|
+
return this.http.request("GET", `/tasks/${id}`);
|
|
424
|
+
}
|
|
425
|
+
cancel(id) {
|
|
426
|
+
return this.http.request("POST", `/tasks/${id}/cancel`);
|
|
427
|
+
}
|
|
428
|
+
async wait(id, options = {}) {
|
|
429
|
+
const intervalMs = options.intervalMs ?? 500;
|
|
430
|
+
const deadline = Date.now() + (options.timeoutMs ?? 12e4);
|
|
431
|
+
for (;;) {
|
|
432
|
+
const { data } = await this.get(id);
|
|
433
|
+
if (TERMINAL.has(data.status)) return data;
|
|
434
|
+
if (Date.now() > deadline) throw new BylineApiError(408, "TASK_TIMEOUT", `Task ${id} did not finish in time (last status: ${data.status})`, `/tasks/${id}`);
|
|
435
|
+
await sleep(intervalMs, options.signal);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
var PublishJobsResource = class {
|
|
440
|
+
http;
|
|
441
|
+
tasks;
|
|
442
|
+
constructor(http, tasks) {
|
|
443
|
+
this.http = http;
|
|
444
|
+
this.tasks = tasks;
|
|
445
|
+
}
|
|
446
|
+
create(input) {
|
|
447
|
+
return this.http.request("POST", "/publish-jobs", { body: input });
|
|
448
|
+
}
|
|
449
|
+
async run(input, options) {
|
|
450
|
+
const { data: ref } = await this.create(input);
|
|
451
|
+
const task = await this.tasks.wait(ref.taskId, options);
|
|
452
|
+
if (task.status !== "completed" || !task.result?.articleId) throw new BylineApiError(500, "PUBLISH_FAILED", `Publish job ${ref.taskId} ended with ${task.status}${task.error ? `: ${task.error}` : ""}`, "/publish-jobs", { task });
|
|
453
|
+
return task;
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region src/resources/users.ts
|
|
458
|
+
var UsersResource = class {
|
|
459
|
+
http;
|
|
460
|
+
constructor(http) {
|
|
461
|
+
this.http = http;
|
|
462
|
+
}
|
|
463
|
+
allowLogin() {
|
|
464
|
+
return this.http.request("GET", "/users/allow-login");
|
|
465
|
+
}
|
|
466
|
+
checkLogged() {
|
|
467
|
+
return this.http.request("GET", "/users/check-logged");
|
|
468
|
+
}
|
|
469
|
+
me() {
|
|
470
|
+
return this.http.request("GET", "/users/me");
|
|
471
|
+
}
|
|
472
|
+
updateMe(input) {
|
|
473
|
+
return this.http.request("PATCH", "/users/me", { body: input });
|
|
474
|
+
}
|
|
475
|
+
list(query = {}) {
|
|
476
|
+
return this.http.request("GET", "/users", { query: { ...query } });
|
|
477
|
+
}
|
|
478
|
+
async listAll(query = {}) {
|
|
479
|
+
const users = [];
|
|
480
|
+
for (let page = 1;; page++) {
|
|
481
|
+
const { data, meta } = await this.list({
|
|
482
|
+
...query,
|
|
483
|
+
page,
|
|
484
|
+
size: 100
|
|
485
|
+
});
|
|
486
|
+
users.push(...data);
|
|
487
|
+
if (!meta || page >= meta.pagination.totalPages || !data.length) break;
|
|
488
|
+
}
|
|
489
|
+
return users;
|
|
490
|
+
}
|
|
491
|
+
get(id) {
|
|
492
|
+
return this.http.request("GET", `/users/${id}`);
|
|
493
|
+
}
|
|
494
|
+
update(id, input) {
|
|
495
|
+
return this.http.request("PATCH", `/users/${id}`, { body: input });
|
|
496
|
+
}
|
|
497
|
+
createProfileOnly(input) {
|
|
498
|
+
return this.http.request("POST", "/users/profile-only", { body: input });
|
|
499
|
+
}
|
|
500
|
+
deactivate(id) {
|
|
501
|
+
return this.http.request("POST", `/users/${id}/deactivate`);
|
|
502
|
+
}
|
|
503
|
+
reactivate(id) {
|
|
504
|
+
return this.http.request("POST", `/users/${id}/reactivate`);
|
|
505
|
+
}
|
|
506
|
+
invite(id, input = {}) {
|
|
507
|
+
return this.http.request("POST", `/users/${id}/invite`, { body: input });
|
|
508
|
+
}
|
|
509
|
+
initFirstUser(input) {
|
|
510
|
+
return this.http.request("POST", "/init/user", { body: input });
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var InvitesResource = class {
|
|
514
|
+
http;
|
|
515
|
+
constructor(http) {
|
|
516
|
+
this.http = http;
|
|
517
|
+
}
|
|
518
|
+
list() {
|
|
519
|
+
return this.http.request("GET", "/users/invites");
|
|
520
|
+
}
|
|
521
|
+
create(input) {
|
|
522
|
+
return this.http.request("POST", "/users/invites", { body: input });
|
|
523
|
+
}
|
|
524
|
+
revoke(id) {
|
|
525
|
+
return this.http.request("DELETE", `/users/invites/${id}`);
|
|
526
|
+
}
|
|
527
|
+
get(token) {
|
|
528
|
+
return this.http.request("GET", `/invites/${encodeURIComponent(token)}`);
|
|
529
|
+
}
|
|
530
|
+
accept(token, input) {
|
|
531
|
+
return this.http.request("POST", `/invites/${encodeURIComponent(token)}/accept`, { body: input });
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
//#endregion
|
|
535
|
+
//#region src/client.ts
|
|
536
|
+
var BylineClient = class {
|
|
537
|
+
http;
|
|
538
|
+
posts;
|
|
539
|
+
drafts;
|
|
540
|
+
publishJobs;
|
|
541
|
+
tasks;
|
|
542
|
+
categories;
|
|
543
|
+
translations;
|
|
544
|
+
users;
|
|
545
|
+
authors;
|
|
546
|
+
invites;
|
|
547
|
+
dynamicComponents;
|
|
548
|
+
files;
|
|
549
|
+
constructor(options) {
|
|
550
|
+
this.http = new HttpCore(options);
|
|
551
|
+
this.drafts = new DraftsResource(this.http);
|
|
552
|
+
this.tasks = new TasksResource(this.http);
|
|
553
|
+
this.publishJobs = new PublishJobsResource(this.http, this.tasks);
|
|
554
|
+
this.posts = new PostsResource(this.http, this.drafts, this.publishJobs);
|
|
555
|
+
this.categories = new CategoriesResource(this.http);
|
|
556
|
+
this.translations = new TranslationsResource(this.http);
|
|
557
|
+
this.users = new UsersResource(this.http);
|
|
558
|
+
this.authors = new AuthorsResource(this.http);
|
|
559
|
+
this.invites = new InvitesResource(this.http);
|
|
560
|
+
this.dynamicComponents = new DynamicComponentsResource(this.http);
|
|
561
|
+
this.files = new FilesResource(this.http);
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
const createClient = (options) => new BylineClient(options);
|
|
565
|
+
//#endregion
|
|
566
|
+
export { BylineApiError, BylineClient, HttpCore, camelcaseKeys, createClient, isBylineApiError, toDraftData };
|
package/package.json
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mx-space/byline-api-client",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed fetch client for the Byline headless CMS API",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"byline",
|
|
7
|
+
"headless-cms",
|
|
8
|
+
"api-client",
|
|
9
|
+
"lexical"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/mx-space/byline/tree/main/packages/api-client#readme",
|
|
12
|
+
"bugs": "https://github.com/mx-space/byline/issues",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/mx-space/byline.git",
|
|
16
|
+
"directory": "packages/api-client"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Innei",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20.0.0"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./dist/index.d.cts",
|
|
38
|
+
"default": "./dist/index.cjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"./package.json": "./package.json"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"tsdown": "0.22.8",
|
|
48
|
+
"typescript": "6.0.3",
|
|
49
|
+
"vitest": "5.0.5"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsdown",
|
|
53
|
+
"lint": "eslint src __tests__ --fix",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
56
|
+
}
|
|
6
57
|
}
|