@mx-space/byline-api-client 0.1.1 → 0.2.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
@@ -23,8 +23,12 @@ const byline = createClient({
23
23
 
24
24
  const { data: posts, meta } = await byline.posts.list({ size: 20 })
25
25
  const { data: post } = await byline.posts.getBySlug('product', 'hello-world')
26
+ const { data: same } = await byline.posts.getBySlugOnly('hello-world')
26
27
  ```
27
28
 
29
+ `getBySlugOnly` reads a post by its slug alone (`GET /posts/slug/:slug`, one request);
30
+ old slugs resolve to the current post.
31
+
28
32
  `endpoint` is the server's API prefix (`/api/v{version}` in production). Every method
29
33
  resolves to `{ data, meta? }`, mirroring the server envelope. Responses are converted
30
34
  from the snake_case wire format to camelCase, except free-form subtrees (post `meta`,
@@ -78,8 +82,10 @@ const admin = createClient({ endpoint, apiKey: process.env.BYLINE_API_KEY })
78
82
 
79
83
  `posts.create` and `posts.revise` go through the draft and publish-job flow the admin
80
84
  uses: they save a draft revision, start a publish job and wait for it. `revise`
81
- branches from the post's published revision, so fields you omit keep their current
82
- values.
85
+ writes into the post's working copy on top of its published revision, so fields you
86
+ omit keep their current values. When it publishes, `revise` refuses with
87
+ `DRAFT_WORKING_COPY_AHEAD` (409) if someone has unpublished changes in the working
88
+ copy; pass `{ publish: false }` to write into the working copy without publishing.
83
89
 
84
90
  ```ts
85
91
  const { postId } = await admin.posts.create(
@@ -134,7 +140,7 @@ invite to that user too (`invite.userId`).
134
140
 
135
141
  | Resource | Methods |
136
142
  | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
137
- | `posts` | `list`, `get`, `getBySlug`, `getPath`, `findBySlug`, `latest`, `create`, `revise`, `update`, `setPublished`, `delete` |
143
+ | `posts` | `list`, `get`, `getBySlug`, `getBySlugOnly`, `getPath`, `findBySlug`, `latest`, `create`, `revise`, `update`, `setPublished`, `delete` |
138
144
  | `authors` | `list`, `listAll`, `get` |
139
145
  | `drafts` | `list`, `get`, `context`, `create`, `update`, `delete` |
140
146
  | `publishJobs` | `create`, `run` (create and wait) |
@@ -167,13 +173,9 @@ export const loadBlogIndex = async (locale: string, page = 1) => {
167
173
  return { posts, pagination: meta?.pagination }
168
174
  }
169
175
 
170
- export const loadBlogPost = async (
171
- locale: string,
172
- category: string,
173
- slug: string,
174
- ) => {
176
+ export const loadBlogPost = async (locale: string, slug: string) => {
175
177
  try {
176
- const { data: post, meta } = await byline.posts.getBySlug(category, slug, {
178
+ const { data: post, meta } = await byline.posts.getBySlugOnly(slug, {
177
179
  lang: locale,
178
180
  })
179
181
  return { post, seo: post.seo, translation: meta?.translation }
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region src/case.ts
2
+ // src/case.ts
3
3
  const isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
4
4
  const camelcase = (key) => key.replaceAll(/_([\da-z])/gi, (_, char) => char.toUpperCase());
5
5
  const splitPath = (path) => path.replaceAll("[]", ".[]").split(".").filter((segment) => segment.length > 0);
@@ -38,8 +38,7 @@ const walk = (value, tree) => {
38
38
  return out;
39
39
  };
40
40
  const camelcaseKeys = (value, preservePaths = []) => walk(value, buildTree(preservePaths));
41
- //#endregion
42
- //#region src/error.ts
41
+ // src/error.ts
43
42
  var BylineApiError = class extends Error {
44
43
  status;
45
44
  code;
@@ -55,8 +54,7 @@ var BylineApiError = class extends Error {
55
54
  }
56
55
  };
57
56
  const isBylineApiError = (error, code) => error instanceof BylineApiError && (code === void 0 || error.code === code);
58
- //#endregion
59
- //#region src/http.ts
57
+ // src/http.ts
60
58
  const ENVELOPE_KEYS = /* @__PURE__ */ new Set(["data", "meta"]);
61
59
  const isEnvelope = (body) => !!body && typeof body === "object" && !Array.isArray(body) && "data" in body && Object.keys(body).every((key) => ENVELOPE_KEYS.has(key));
62
60
  const isBinaryBody = (body) => typeof FormData !== "undefined" && body instanceof FormData || typeof Blob !== "undefined" && body instanceof Blob || body instanceof ArrayBuffer || body instanceof URLSearchParams || typeof body === "string";
@@ -125,8 +123,7 @@ var HttpCore = class {
125
123
  return new BylineApiError(status, "HTTP_ERROR", typeof payload === "string" && payload ? payload.slice(0, 500) : `Request failed with status ${status}`, path);
126
124
  }
127
125
  };
128
- //#endregion
129
- //#region src/resources/authors.ts
126
+ // src/resources/authors.ts
130
127
  var AuthorsResource = class {
131
128
  http;
132
129
  constructor(http) {
@@ -151,8 +148,7 @@ var AuthorsResource = class {
151
148
  return this.http.request("GET", `/authors/${encodeURIComponent(idOrUsername)}`);
152
149
  }
153
150
  };
154
- //#endregion
155
- //#region src/resources/content.ts
151
+ // src/resources/content.ts
156
152
  const CATALOG_PRESERVE = ["components[].propsSchema"];
157
153
  var CategoriesResource = class {
158
154
  http;
@@ -219,6 +215,15 @@ var DynamicComponentsResource = class {
219
215
  preserve: CATALOG_PRESERVE
220
216
  });
221
217
  }
218
+ update(id, input) {
219
+ return this.http.request("PATCH", `/dynamic-components/${encodeURIComponent(id)}`, {
220
+ body: input,
221
+ preserve: CATALOG_PRESERVE
222
+ });
223
+ }
224
+ remove(id) {
225
+ return this.http.request("DELETE", `/dynamic-components/${encodeURIComponent(id)}`, { preserve: CATALOG_PRESERVE });
226
+ }
222
227
  };
223
228
  var FilesResource = class {
224
229
  http;
@@ -238,8 +243,7 @@ var FilesResource = class {
238
243
  });
239
244
  }
240
245
  };
241
- //#endregion
242
- //#region src/resources/drafts.ts
246
+ // src/resources/drafts.ts
243
247
  var DraftsResource = class {
244
248
  http;
245
249
  constructor(http) {
@@ -264,8 +268,7 @@ var DraftsResource = class {
264
268
  return this.http.request("DELETE", `/drafts/${id}`);
265
269
  }
266
270
  };
267
- //#endregion
268
- //#region src/resources/posts.ts
271
+ // src/resources/posts.ts
269
272
  const POST_PRESERVE = ["meta"];
270
273
  const POST_LIST_PRESERVE = ["[].meta"];
271
274
  const DRAFT_CORE_KEYS = /* @__PURE__ */ new Set([
@@ -331,6 +334,12 @@ var PostsResource = class {
331
334
  preserve: POST_PRESERVE
332
335
  });
333
336
  }
337
+ getBySlugOnly(slug, options = {}) {
338
+ return this.http.request("GET", `/posts/slug/${encodeURIComponent(slug)}`, {
339
+ query: { lang: options.lang ?? this.http.lang },
340
+ preserve: POST_PRESERVE
341
+ });
342
+ }
334
343
  getPath(slug) {
335
344
  return this.http.request("GET", `/posts/get-url/${encodeURIComponent(slug)}`);
336
345
  }
@@ -375,7 +384,8 @@ var PostsResource = class {
375
384
  refType: "post",
376
385
  refId: id,
377
386
  baseRevisionId: base.id,
378
- data: mergeSnapshot(base, toDraftData(input))
387
+ data: mergeSnapshot(base, toDraftData(input)),
388
+ ...options.publish === false ? {} : { ifInSync: true }
379
389
  });
380
390
  return this.finish(draft, options);
381
391
  }
@@ -389,7 +399,6 @@ var PostsResource = class {
389
399
  branchId: draft.id,
390
400
  revisionId: draft.headRevisionId,
391
401
  expectedPublishedRevisionId: draft.document.publishedRevisionId,
392
- confirmDiverged: draft.relationToPublished === "descendant" || draft.relationToPublished === "diverged",
393
402
  aiResources: options.aiResources,
394
403
  regenerateLockedTranslations: options.regenerateLockedTranslations
395
404
  }, options);
@@ -400,8 +409,7 @@ var PostsResource = class {
400
409
  };
401
410
  }
402
411
  };
403
- //#endregion
404
- //#region src/resources/tasks.ts
412
+ // src/resources/tasks.ts
405
413
  const TERMINAL = /* @__PURE__ */ new Set([
406
414
  "completed",
407
415
  "partial_failed",
@@ -454,8 +462,7 @@ var PublishJobsResource = class {
454
462
  return task;
455
463
  }
456
464
  };
457
- //#endregion
458
- //#region src/resources/users.ts
465
+ // src/resources/users.ts
459
466
  var UsersResource = class {
460
467
  http;
461
468
  constructor(http) {
@@ -532,8 +539,7 @@ var InvitesResource = class {
532
539
  return this.http.request("POST", `/invites/${encodeURIComponent(token)}/accept`, { body: input });
533
540
  }
534
541
  };
535
- //#endregion
536
- //#region src/client.ts
542
+ // src/client.ts
537
543
  var BylineClient = class {
538
544
  http;
539
545
  posts;
@@ -563,7 +569,6 @@ var BylineClient = class {
563
569
  }
564
570
  };
565
571
  const createClient = (options) => new BylineClient(options);
566
- //#endregion
567
572
  exports.BylineApiError = BylineApiError;
568
573
  exports.BylineClient = BylineClient;
569
574
  exports.HttpCore = HttpCore;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,4 @@
1
- //#region src/case.d.ts
2
- declare const camelcaseKeys: <T>(value: unknown, preservePaths?: readonly string[]) => T;
3
- //#endregion
4
- //#region src/types.d.ts
1
+ export declare const camelcaseKeys: <T>(value: unknown, preservePaths?: readonly string[]) => T;
5
2
  interface ApiResponse<T, M = Record<string, unknown>> {
6
3
  data: T;
7
4
  meta?: M;
@@ -172,12 +169,13 @@ interface PostImage {
172
169
  [key: string]: unknown;
173
170
  }
174
171
  interface Post {
172
+ publicAt?: string | null;
175
173
  id: string;
176
174
  title: string;
177
175
  slug: string;
178
176
  text: string;
179
177
  content: string | null;
180
- contentFormat: string;
178
+ contentFormat: 'lexical';
181
179
  summary: string | null;
182
180
  images: PostImage[] | null;
183
181
  meta: Record<string, unknown> | null;
@@ -195,7 +193,7 @@ interface Post {
195
193
  seo: PostSeo | null;
196
194
  [key: string]: unknown;
197
195
  }
198
- type PostSortKey = 'createdAt' | 'modifiedAt' | 'pinAt';
196
+ type PostSortKey = 'createdAt' | 'modifiedAt' | 'pinAt' | 'publicAt';
199
197
  interface PostListQuery {
200
198
  page?: number;
201
199
  size?: number;
@@ -208,6 +206,7 @@ interface PostListQuery {
208
206
  lang?: string;
209
207
  }
210
208
  interface PostPatch {
209
+ publicAt?: string | null;
211
210
  categoryId?: string;
212
211
  pinAt?: string | null;
213
212
  authors?: string[];
@@ -216,6 +215,7 @@ interface PostPatch {
216
215
  seo?: PostSeo | null;
217
216
  }
218
217
  interface PostWriteInput {
218
+ publicAt?: string | null;
219
219
  title: string;
220
220
  content: string | LexicalEditorState;
221
221
  text?: string;
@@ -234,10 +234,9 @@ interface PostWriteInput {
234
234
  createdAt?: string;
235
235
  }
236
236
  type DraftRefType = 'post';
237
- type RevisionRelation = 'same' | 'ancestor' | 'descendant' | 'diverged';
238
237
  interface RevisionSnapshot {
239
238
  content: string | null;
240
- contentFormat: string;
239
+ contentFormat: 'lexical';
241
240
  images: unknown[] | null;
242
241
  meta: Record<string, unknown> | null;
243
242
  text: string;
@@ -256,9 +255,14 @@ interface ContentDocument {
256
255
  refType: DraftRefType;
257
256
  publishedRevisionId: string | null;
258
257
  publishAiResources: PublishAiResourceRequest[] | null;
258
+ aiSeo: {
259
+ description: string;
260
+ sourceHash: string;
261
+ } | null;
259
262
  createdAt: string;
260
263
  updatedAt: string | null;
261
264
  }
265
+ type DraftRelation = 'ahead' | 'fresh' | 'in_sync';
262
266
  interface DraftBranch {
263
267
  id: string;
264
268
  documentId: string;
@@ -269,7 +273,7 @@ interface DraftBranch {
269
273
  headRevision: ContentRevision;
270
274
  baseRevision: ContentRevision;
271
275
  publishedRevision: ContentRevision | null;
272
- relationToPublished: RevisionRelation | null;
276
+ relation: DraftRelation;
273
277
  createdAt: string;
274
278
  updatedAt: string | null;
275
279
  }
@@ -293,8 +297,9 @@ interface CreateDraftInput {
293
297
  refId?: string;
294
298
  baseRevisionId?: string | null;
295
299
  data: DraftWriteData;
300
+ ifInSync?: boolean;
296
301
  }
297
- type PublishAiResource = 'insights' | 'summary' | 'translation' | 'tts';
302
+ type PublishAiResource = 'insights' | 'seo' | 'summary' | 'translation' | 'tts';
298
303
  interface PublishAiResourceRequest {
299
304
  resource: PublishAiResource;
300
305
  mode: 'sync' | 'async';
@@ -343,7 +348,7 @@ interface Translation {
343
348
  summary: string | null;
344
349
  tags: string[];
345
350
  content: string | null;
346
- contentFormat: string | null;
351
+ contentFormat: 'lexical' | null;
347
352
  isLocked: boolean;
348
353
  aiModel: string | null;
349
354
  aiProvider: string | null;
@@ -383,12 +388,14 @@ interface TranslationTaskResult {
383
388
  skippedLocked: string[];
384
389
  }
385
390
  interface DynamicComponent {
391
+ id: string;
386
392
  name: string;
387
393
  description: string;
388
394
  url: string;
389
395
  initialHeight: number;
390
396
  propsSchema: Record<string, unknown>;
391
397
  parentUrl?: string;
398
+ previousUrls: string[];
392
399
  }
393
400
  interface DynamicComponentCatalog {
394
401
  version: 1;
@@ -402,6 +409,9 @@ interface RegisterDynamicComponentInput {
402
409
  propsSchema?: Record<string, unknown>;
403
410
  parentUrl?: string;
404
411
  }
412
+ type UpdateDynamicComponentInput = Partial<Omit<RegisterDynamicComponentInput, 'parentUrl'>> & {
413
+ parentUrl?: string | null;
414
+ };
405
415
  type FileType = 'image' | 'file' | 'avatar' | 'icon' | 'video' | 'audio';
406
416
  interface UploadedFile {
407
417
  url: string;
@@ -409,8 +419,6 @@ interface UploadedFile {
409
419
  storageBackend?: string;
410
420
  storageKey?: string;
411
421
  }
412
- //#endregion
413
- //#region src/http.d.ts
414
422
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
415
423
  type QueryValue = string | number | boolean | null | undefined | ReadonlyArray<string | number>;
416
424
  interface ClientOptions {
@@ -429,7 +437,7 @@ interface RequestOptions {
429
437
  preserve?: readonly string[];
430
438
  signal?: AbortSignal;
431
439
  }
432
- declare class HttpCore {
440
+ export declare class HttpCore {
433
441
  private readonly options;
434
442
  readonly endpoint: string;
435
443
  private readonly fetchImpl;
@@ -440,8 +448,6 @@ declare class HttpCore {
440
448
  private transform;
441
449
  private toError;
442
450
  }
443
- //#endregion
444
- //#region src/resources/authors.d.ts
445
451
  declare class AuthorsResource {
446
452
  private readonly http;
447
453
  constructor(http: HttpCore);
@@ -449,8 +455,6 @@ declare class AuthorsResource {
449
455
  listAll(): Promise<Author[]>;
450
456
  get(idOrUsername: string): Promise<ApiResponse<Author, Record<string, unknown>>>;
451
457
  }
452
- //#endregion
453
- //#region src/resources/content.d.ts
454
458
  declare class CategoriesResource {
455
459
  private readonly http;
456
460
  constructor(http: HttpCore);
@@ -492,6 +496,8 @@ declare class DynamicComponentsResource {
492
496
  constructor(http: HttpCore);
493
497
  list(): Promise<ApiResponse<DynamicComponentCatalog, Record<string, unknown>>>;
494
498
  register(input: RegisterDynamicComponentInput): Promise<ApiResponse<DynamicComponentCatalog, Record<string, unknown>>>;
499
+ update(id: string, input: UpdateDynamicComponentInput): Promise<ApiResponse<DynamicComponentCatalog, Record<string, unknown>>>;
500
+ remove(id: string): Promise<ApiResponse<DynamicComponentCatalog, Record<string, unknown>>>;
495
501
  }
496
502
  declare class FilesResource {
497
503
  private readonly http;
@@ -502,8 +508,6 @@ declare class FilesResource {
502
508
  immutable?: boolean;
503
509
  }): Promise<ApiResponse<UploadedFile, Record<string, unknown>>>;
504
510
  }
505
- //#endregion
506
- //#region src/resources/drafts.d.ts
507
511
  declare class DraftsResource {
508
512
  private readonly http;
509
513
  constructor(http: HttpCore);
@@ -523,8 +527,6 @@ declare class DraftsResource {
523
527
  }): Promise<ApiResponse<DraftBranch, Record<string, unknown>>>;
524
528
  delete(id: string): Promise<ApiResponse<void, Record<string, unknown>>>;
525
529
  }
526
- //#endregion
527
- //#region src/resources/tasks.d.ts
528
530
  interface WaitForTaskOptions {
529
531
  intervalMs?: number;
530
532
  timeoutMs?: number;
@@ -546,8 +548,6 @@ declare class PublishJobsResource {
546
548
  result: PublishTaskResult;
547
549
  }>;
548
550
  }
549
- //#endregion
550
- //#region src/resources/posts.d.ts
551
551
  interface PostSaveOptions extends WaitForTaskOptions {
552
552
  publish?: boolean;
553
553
  aiResources?: Array<PublishAiResource | PublishAiResourceRequest>;
@@ -558,7 +558,7 @@ interface PostSaveResult {
558
558
  postId: string | null;
559
559
  taskId: string | null;
560
560
  }
561
- declare const toDraftData: (input: Partial<PostWriteInput>) => DraftWriteData;
561
+ export declare const toDraftData: (input: Partial<PostWriteInput>) => DraftWriteData;
562
562
  declare class PostsResource {
563
563
  private readonly http;
564
564
  private readonly drafts;
@@ -571,6 +571,9 @@ declare class PostsResource {
571
571
  getBySlug(categorySlug: string, slug: string, options?: {
572
572
  lang?: string;
573
573
  }): Promise<ApiResponse<Post, PostResponseMeta>>;
574
+ getBySlugOnly(slug: string, options?: {
575
+ lang?: string;
576
+ }): Promise<ApiResponse<Post, PostResponseMeta>>;
574
577
  getPath(slug: string): Promise<ApiResponse<{
575
578
  path: string;
576
579
  }, Record<string, unknown>>>;
@@ -589,8 +592,6 @@ declare class PostsResource {
589
592
  revise(id: string, input: Partial<PostWriteInput>, options?: PostSaveOptions): Promise<PostSaveResult>;
590
593
  private finish;
591
594
  }
592
- //#endregion
593
- //#region src/resources/users.d.ts
594
595
  declare class UsersResource {
595
596
  private readonly http;
596
597
  constructor(http: HttpCore);
@@ -620,9 +621,7 @@ declare class InvitesResource {
620
621
  get(token: string): Promise<ApiResponse<PublicInvite, Record<string, unknown>>>;
621
622
  accept(token: string, input: AcceptInviteInput): Promise<ApiResponse<UserDetail, Record<string, unknown>>>;
622
623
  }
623
- //#endregion
624
- //#region src/client.d.ts
625
- declare class BylineClient {
624
+ export declare class BylineClient {
626
625
  readonly http: HttpCore;
627
626
  readonly posts: PostsResource;
628
627
  readonly drafts: DraftsResource;
@@ -637,10 +636,8 @@ declare class BylineClient {
637
636
  readonly files: FilesResource;
638
637
  constructor(options: ClientOptions);
639
638
  }
640
- declare const createClient: (options: ClientOptions) => BylineClient;
641
- //#endregion
642
- //#region src/error.d.ts
643
- declare class BylineApiError extends Error {
639
+ export declare const createClient: (options: ClientOptions) => BylineClient;
640
+ export declare class BylineApiError extends Error {
644
641
  readonly status: number;
645
642
  readonly code: string;
646
643
  readonly path: string;
@@ -648,6 +645,5 @@ declare class BylineApiError extends Error {
648
645
  readonly name = "BylineApiError";
649
646
  constructor(status: number, code: string, message: string, path: string, details?: unknown | undefined);
650
647
  }
651
- declare const isBylineApiError: (error: unknown, code?: string) => error is BylineApiError;
652
- //#endregion
653
- export { type AcceptInviteInput, type AllowLoginResult, type ApiResponse, type ArticleTranslationMeta, type Author, type AuthorListQuery, type AuthorsResource, BylineApiError, BylineClient, type CategoriesResource, type Category, type CategorySummary, type ClientOptions, type ContentDocument, type ContentRevision, type CreateDraftInput, type CreateInviteInput, type CreatePublishJobInput, type CreatedInvite, type DraftBranch, type DraftRefType, type DraftWriteData, type DraftsResource, type DynamicComponent, type DynamicComponentCatalog, type DynamicComponentsResource, type EntryTranslationMeta, type FileType, type FilesResource, HttpCore, type HttpMethod, type InitUserInput, type Invite, type InviteUserInput, type InvitesResource, type LexicalEditorState, type ManualTranslationInput, type PaginatedMeta, type Pagination, type Post, type PostImage, type PostListQuery, type PostPatch, type PostResponseMeta, type PostSaveOptions, type PostSaveResult, type PostSeo, type PostSortKey, type PostWriteInput, type PostsResource, type ProfileOnlyUserInput, type PublicAuthor, type PublicInvite, type PublishAiResource, type PublishAiResourceRequest, type PublishJobsResource, type PublishTaskResult, type QueryValue, type RegisterDynamicComponentInput, type RequestOptions, type RevisionRelation, type RevisionSnapshot, type Task, type TaskRef, type TaskStatus, type TasksResource, type Translation, type TranslationPatch, type TranslationTaskInput, type TranslationTaskResult, type TranslationsResource, type UploadedFile, type UserDetail, type UserListQuery, type UserListStatus, type UserProfileInput, type UsersResource, type VersionContext, type WaitForTaskOptions, camelcaseKeys, createClient, isBylineApiError, toDraftData };
648
+ export declare const isBylineApiError: (error: unknown, code?: string) => error is BylineApiError;
649
+ export type { AcceptInviteInput, AllowLoginResult, ApiResponse, ArticleTranslationMeta, Author, AuthorListQuery, AuthorsResource, CategoriesResource, Category, CategorySummary, ClientOptions, ContentDocument, ContentRevision, CreateDraftInput, CreateInviteInput, CreatePublishJobInput, CreatedInvite, DraftBranch, DraftRefType, DraftRelation, DraftWriteData, DraftsResource, DynamicComponent, DynamicComponentCatalog, DynamicComponentsResource, EntryTranslationMeta, FileType, FilesResource, HttpMethod, InitUserInput, Invite, InviteUserInput, InvitesResource, LexicalEditorState, ManualTranslationInput, PaginatedMeta, Pagination, Post, PostImage, PostListQuery, PostPatch, PostResponseMeta, PostSaveOptions, PostSaveResult, PostSeo, PostSortKey, PostWriteInput, PostsResource, ProfileOnlyUserInput, PublicAuthor, PublicInvite, PublishAiResource, PublishAiResourceRequest, PublishJobsResource, PublishTaskResult, QueryValue, RegisterDynamicComponentInput, RequestOptions, RevisionSnapshot, Task, TaskRef, TaskStatus, TasksResource, Translation, TranslationPatch, TranslationTaskInput, TranslationTaskResult, TranslationsResource, UpdateDynamicComponentInput, UploadedFile, UserDetail, UserListQuery, UserListStatus, UserProfileInput, UsersResource, VersionContext, WaitForTaskOptions };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,4 @@
1
- //#region src/case.d.ts
2
- declare const camelcaseKeys: <T>(value: unknown, preservePaths?: readonly string[]) => T;
3
- //#endregion
4
- //#region src/types.d.ts
1
+ export declare const camelcaseKeys: <T>(value: unknown, preservePaths?: readonly string[]) => T;
5
2
  interface ApiResponse<T, M = Record<string, unknown>> {
6
3
  data: T;
7
4
  meta?: M;
@@ -172,12 +169,13 @@ interface PostImage {
172
169
  [key: string]: unknown;
173
170
  }
174
171
  interface Post {
172
+ publicAt?: string | null;
175
173
  id: string;
176
174
  title: string;
177
175
  slug: string;
178
176
  text: string;
179
177
  content: string | null;
180
- contentFormat: string;
178
+ contentFormat: 'lexical';
181
179
  summary: string | null;
182
180
  images: PostImage[] | null;
183
181
  meta: Record<string, unknown> | null;
@@ -195,7 +193,7 @@ interface Post {
195
193
  seo: PostSeo | null;
196
194
  [key: string]: unknown;
197
195
  }
198
- type PostSortKey = 'createdAt' | 'modifiedAt' | 'pinAt';
196
+ type PostSortKey = 'createdAt' | 'modifiedAt' | 'pinAt' | 'publicAt';
199
197
  interface PostListQuery {
200
198
  page?: number;
201
199
  size?: number;
@@ -208,6 +206,7 @@ interface PostListQuery {
208
206
  lang?: string;
209
207
  }
210
208
  interface PostPatch {
209
+ publicAt?: string | null;
211
210
  categoryId?: string;
212
211
  pinAt?: string | null;
213
212
  authors?: string[];
@@ -216,6 +215,7 @@ interface PostPatch {
216
215
  seo?: PostSeo | null;
217
216
  }
218
217
  interface PostWriteInput {
218
+ publicAt?: string | null;
219
219
  title: string;
220
220
  content: string | LexicalEditorState;
221
221
  text?: string;
@@ -234,10 +234,9 @@ interface PostWriteInput {
234
234
  createdAt?: string;
235
235
  }
236
236
  type DraftRefType = 'post';
237
- type RevisionRelation = 'same' | 'ancestor' | 'descendant' | 'diverged';
238
237
  interface RevisionSnapshot {
239
238
  content: string | null;
240
- contentFormat: string;
239
+ contentFormat: 'lexical';
241
240
  images: unknown[] | null;
242
241
  meta: Record<string, unknown> | null;
243
242
  text: string;
@@ -256,9 +255,14 @@ interface ContentDocument {
256
255
  refType: DraftRefType;
257
256
  publishedRevisionId: string | null;
258
257
  publishAiResources: PublishAiResourceRequest[] | null;
258
+ aiSeo: {
259
+ description: string;
260
+ sourceHash: string;
261
+ } | null;
259
262
  createdAt: string;
260
263
  updatedAt: string | null;
261
264
  }
265
+ type DraftRelation = 'ahead' | 'fresh' | 'in_sync';
262
266
  interface DraftBranch {
263
267
  id: string;
264
268
  documentId: string;
@@ -269,7 +273,7 @@ interface DraftBranch {
269
273
  headRevision: ContentRevision;
270
274
  baseRevision: ContentRevision;
271
275
  publishedRevision: ContentRevision | null;
272
- relationToPublished: RevisionRelation | null;
276
+ relation: DraftRelation;
273
277
  createdAt: string;
274
278
  updatedAt: string | null;
275
279
  }
@@ -293,8 +297,9 @@ interface CreateDraftInput {
293
297
  refId?: string;
294
298
  baseRevisionId?: string | null;
295
299
  data: DraftWriteData;
300
+ ifInSync?: boolean;
296
301
  }
297
- type PublishAiResource = 'insights' | 'summary' | 'translation' | 'tts';
302
+ type PublishAiResource = 'insights' | 'seo' | 'summary' | 'translation' | 'tts';
298
303
  interface PublishAiResourceRequest {
299
304
  resource: PublishAiResource;
300
305
  mode: 'sync' | 'async';
@@ -343,7 +348,7 @@ interface Translation {
343
348
  summary: string | null;
344
349
  tags: string[];
345
350
  content: string | null;
346
- contentFormat: string | null;
351
+ contentFormat: 'lexical' | null;
347
352
  isLocked: boolean;
348
353
  aiModel: string | null;
349
354
  aiProvider: string | null;
@@ -383,12 +388,14 @@ interface TranslationTaskResult {
383
388
  skippedLocked: string[];
384
389
  }
385
390
  interface DynamicComponent {
391
+ id: string;
386
392
  name: string;
387
393
  description: string;
388
394
  url: string;
389
395
  initialHeight: number;
390
396
  propsSchema: Record<string, unknown>;
391
397
  parentUrl?: string;
398
+ previousUrls: string[];
392
399
  }
393
400
  interface DynamicComponentCatalog {
394
401
  version: 1;
@@ -402,6 +409,9 @@ interface RegisterDynamicComponentInput {
402
409
  propsSchema?: Record<string, unknown>;
403
410
  parentUrl?: string;
404
411
  }
412
+ type UpdateDynamicComponentInput = Partial<Omit<RegisterDynamicComponentInput, 'parentUrl'>> & {
413
+ parentUrl?: string | null;
414
+ };
405
415
  type FileType = 'image' | 'file' | 'avatar' | 'icon' | 'video' | 'audio';
406
416
  interface UploadedFile {
407
417
  url: string;
@@ -409,8 +419,6 @@ interface UploadedFile {
409
419
  storageBackend?: string;
410
420
  storageKey?: string;
411
421
  }
412
- //#endregion
413
- //#region src/http.d.ts
414
422
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
415
423
  type QueryValue = string | number | boolean | null | undefined | ReadonlyArray<string | number>;
416
424
  interface ClientOptions {
@@ -429,7 +437,7 @@ interface RequestOptions {
429
437
  preserve?: readonly string[];
430
438
  signal?: AbortSignal;
431
439
  }
432
- declare class HttpCore {
440
+ export declare class HttpCore {
433
441
  private readonly options;
434
442
  readonly endpoint: string;
435
443
  private readonly fetchImpl;
@@ -440,8 +448,6 @@ declare class HttpCore {
440
448
  private transform;
441
449
  private toError;
442
450
  }
443
- //#endregion
444
- //#region src/resources/authors.d.ts
445
451
  declare class AuthorsResource {
446
452
  private readonly http;
447
453
  constructor(http: HttpCore);
@@ -449,8 +455,6 @@ declare class AuthorsResource {
449
455
  listAll(): Promise<Author[]>;
450
456
  get(idOrUsername: string): Promise<ApiResponse<Author, Record<string, unknown>>>;
451
457
  }
452
- //#endregion
453
- //#region src/resources/content.d.ts
454
458
  declare class CategoriesResource {
455
459
  private readonly http;
456
460
  constructor(http: HttpCore);
@@ -492,6 +496,8 @@ declare class DynamicComponentsResource {
492
496
  constructor(http: HttpCore);
493
497
  list(): Promise<ApiResponse<DynamicComponentCatalog, Record<string, unknown>>>;
494
498
  register(input: RegisterDynamicComponentInput): Promise<ApiResponse<DynamicComponentCatalog, Record<string, unknown>>>;
499
+ update(id: string, input: UpdateDynamicComponentInput): Promise<ApiResponse<DynamicComponentCatalog, Record<string, unknown>>>;
500
+ remove(id: string): Promise<ApiResponse<DynamicComponentCatalog, Record<string, unknown>>>;
495
501
  }
496
502
  declare class FilesResource {
497
503
  private readonly http;
@@ -502,8 +508,6 @@ declare class FilesResource {
502
508
  immutable?: boolean;
503
509
  }): Promise<ApiResponse<UploadedFile, Record<string, unknown>>>;
504
510
  }
505
- //#endregion
506
- //#region src/resources/drafts.d.ts
507
511
  declare class DraftsResource {
508
512
  private readonly http;
509
513
  constructor(http: HttpCore);
@@ -523,8 +527,6 @@ declare class DraftsResource {
523
527
  }): Promise<ApiResponse<DraftBranch, Record<string, unknown>>>;
524
528
  delete(id: string): Promise<ApiResponse<void, Record<string, unknown>>>;
525
529
  }
526
- //#endregion
527
- //#region src/resources/tasks.d.ts
528
530
  interface WaitForTaskOptions {
529
531
  intervalMs?: number;
530
532
  timeoutMs?: number;
@@ -546,8 +548,6 @@ declare class PublishJobsResource {
546
548
  result: PublishTaskResult;
547
549
  }>;
548
550
  }
549
- //#endregion
550
- //#region src/resources/posts.d.ts
551
551
  interface PostSaveOptions extends WaitForTaskOptions {
552
552
  publish?: boolean;
553
553
  aiResources?: Array<PublishAiResource | PublishAiResourceRequest>;
@@ -558,7 +558,7 @@ interface PostSaveResult {
558
558
  postId: string | null;
559
559
  taskId: string | null;
560
560
  }
561
- declare const toDraftData: (input: Partial<PostWriteInput>) => DraftWriteData;
561
+ export declare const toDraftData: (input: Partial<PostWriteInput>) => DraftWriteData;
562
562
  declare class PostsResource {
563
563
  private readonly http;
564
564
  private readonly drafts;
@@ -571,6 +571,9 @@ declare class PostsResource {
571
571
  getBySlug(categorySlug: string, slug: string, options?: {
572
572
  lang?: string;
573
573
  }): Promise<ApiResponse<Post, PostResponseMeta>>;
574
+ getBySlugOnly(slug: string, options?: {
575
+ lang?: string;
576
+ }): Promise<ApiResponse<Post, PostResponseMeta>>;
574
577
  getPath(slug: string): Promise<ApiResponse<{
575
578
  path: string;
576
579
  }, Record<string, unknown>>>;
@@ -589,8 +592,6 @@ declare class PostsResource {
589
592
  revise(id: string, input: Partial<PostWriteInput>, options?: PostSaveOptions): Promise<PostSaveResult>;
590
593
  private finish;
591
594
  }
592
- //#endregion
593
- //#region src/resources/users.d.ts
594
595
  declare class UsersResource {
595
596
  private readonly http;
596
597
  constructor(http: HttpCore);
@@ -620,9 +621,7 @@ declare class InvitesResource {
620
621
  get(token: string): Promise<ApiResponse<PublicInvite, Record<string, unknown>>>;
621
622
  accept(token: string, input: AcceptInviteInput): Promise<ApiResponse<UserDetail, Record<string, unknown>>>;
622
623
  }
623
- //#endregion
624
- //#region src/client.d.ts
625
- declare class BylineClient {
624
+ export declare class BylineClient {
626
625
  readonly http: HttpCore;
627
626
  readonly posts: PostsResource;
628
627
  readonly drafts: DraftsResource;
@@ -637,10 +636,8 @@ declare class BylineClient {
637
636
  readonly files: FilesResource;
638
637
  constructor(options: ClientOptions);
639
638
  }
640
- declare const createClient: (options: ClientOptions) => BylineClient;
641
- //#endregion
642
- //#region src/error.d.ts
643
- declare class BylineApiError extends Error {
639
+ export declare const createClient: (options: ClientOptions) => BylineClient;
640
+ export declare class BylineApiError extends Error {
644
641
  readonly status: number;
645
642
  readonly code: string;
646
643
  readonly path: string;
@@ -648,6 +645,5 @@ declare class BylineApiError extends Error {
648
645
  readonly name = "BylineApiError";
649
646
  constructor(status: number, code: string, message: string, path: string, details?: unknown | undefined);
650
647
  }
651
- declare const isBylineApiError: (error: unknown, code?: string) => error is BylineApiError;
652
- //#endregion
653
- export { type AcceptInviteInput, type AllowLoginResult, type ApiResponse, type ArticleTranslationMeta, type Author, type AuthorListQuery, type AuthorsResource, BylineApiError, BylineClient, type CategoriesResource, type Category, type CategorySummary, type ClientOptions, type ContentDocument, type ContentRevision, type CreateDraftInput, type CreateInviteInput, type CreatePublishJobInput, type CreatedInvite, type DraftBranch, type DraftRefType, type DraftWriteData, type DraftsResource, type DynamicComponent, type DynamicComponentCatalog, type DynamicComponentsResource, type EntryTranslationMeta, type FileType, type FilesResource, HttpCore, type HttpMethod, type InitUserInput, type Invite, type InviteUserInput, type InvitesResource, type LexicalEditorState, type ManualTranslationInput, type PaginatedMeta, type Pagination, type Post, type PostImage, type PostListQuery, type PostPatch, type PostResponseMeta, type PostSaveOptions, type PostSaveResult, type PostSeo, type PostSortKey, type PostWriteInput, type PostsResource, type ProfileOnlyUserInput, type PublicAuthor, type PublicInvite, type PublishAiResource, type PublishAiResourceRequest, type PublishJobsResource, type PublishTaskResult, type QueryValue, type RegisterDynamicComponentInput, type RequestOptions, type RevisionRelation, type RevisionSnapshot, type Task, type TaskRef, type TaskStatus, type TasksResource, type Translation, type TranslationPatch, type TranslationTaskInput, type TranslationTaskResult, type TranslationsResource, type UploadedFile, type UserDetail, type UserListQuery, type UserListStatus, type UserProfileInput, type UsersResource, type VersionContext, type WaitForTaskOptions, camelcaseKeys, createClient, isBylineApiError, toDraftData };
648
+ export declare const isBylineApiError: (error: unknown, code?: string) => error is BylineApiError;
649
+ export type { AcceptInviteInput, AllowLoginResult, ApiResponse, ArticleTranslationMeta, Author, AuthorListQuery, AuthorsResource, CategoriesResource, Category, CategorySummary, ClientOptions, ContentDocument, ContentRevision, CreateDraftInput, CreateInviteInput, CreatePublishJobInput, CreatedInvite, DraftBranch, DraftRefType, DraftRelation, DraftWriteData, DraftsResource, DynamicComponent, DynamicComponentCatalog, DynamicComponentsResource, EntryTranslationMeta, FileType, FilesResource, HttpMethod, InitUserInput, Invite, InviteUserInput, InvitesResource, LexicalEditorState, ManualTranslationInput, PaginatedMeta, Pagination, Post, PostImage, PostListQuery, PostPatch, PostResponseMeta, PostSaveOptions, PostSaveResult, PostSeo, PostSortKey, PostWriteInput, PostsResource, ProfileOnlyUserInput, PublicAuthor, PublicInvite, PublishAiResource, PublishAiResourceRequest, PublishJobsResource, PublishTaskResult, QueryValue, RegisterDynamicComponentInput, RequestOptions, RevisionSnapshot, Task, TaskRef, TaskStatus, TasksResource, Translation, TranslationPatch, TranslationTaskInput, TranslationTaskResult, TranslationsResource, UpdateDynamicComponentInput, UploadedFile, UserDetail, UserListQuery, UserListStatus, UserProfileInput, UsersResource, VersionContext, WaitForTaskOptions };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/case.ts
1
+ // src/case.ts
2
2
  const isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
3
3
  const camelcase = (key) => key.replaceAll(/_([\da-z])/gi, (_, char) => char.toUpperCase());
4
4
  const splitPath = (path) => path.replaceAll("[]", ".[]").split(".").filter((segment) => segment.length > 0);
@@ -37,8 +37,7 @@ const walk = (value, tree) => {
37
37
  return out;
38
38
  };
39
39
  const camelcaseKeys = (value, preservePaths = []) => walk(value, buildTree(preservePaths));
40
- //#endregion
41
- //#region src/error.ts
40
+ // src/error.ts
42
41
  var BylineApiError = class extends Error {
43
42
  status;
44
43
  code;
@@ -54,8 +53,7 @@ var BylineApiError = class extends Error {
54
53
  }
55
54
  };
56
55
  const isBylineApiError = (error, code) => error instanceof BylineApiError && (code === void 0 || error.code === code);
57
- //#endregion
58
- //#region src/http.ts
56
+ // src/http.ts
59
57
  const ENVELOPE_KEYS = /* @__PURE__ */ new Set(["data", "meta"]);
60
58
  const isEnvelope = (body) => !!body && typeof body === "object" && !Array.isArray(body) && "data" in body && Object.keys(body).every((key) => ENVELOPE_KEYS.has(key));
61
59
  const isBinaryBody = (body) => typeof FormData !== "undefined" && body instanceof FormData || typeof Blob !== "undefined" && body instanceof Blob || body instanceof ArrayBuffer || body instanceof URLSearchParams || typeof body === "string";
@@ -124,8 +122,7 @@ var HttpCore = class {
124
122
  return new BylineApiError(status, "HTTP_ERROR", typeof payload === "string" && payload ? payload.slice(0, 500) : `Request failed with status ${status}`, path);
125
123
  }
126
124
  };
127
- //#endregion
128
- //#region src/resources/authors.ts
125
+ // src/resources/authors.ts
129
126
  var AuthorsResource = class {
130
127
  http;
131
128
  constructor(http) {
@@ -150,8 +147,7 @@ var AuthorsResource = class {
150
147
  return this.http.request("GET", `/authors/${encodeURIComponent(idOrUsername)}`);
151
148
  }
152
149
  };
153
- //#endregion
154
- //#region src/resources/content.ts
150
+ // src/resources/content.ts
155
151
  const CATALOG_PRESERVE = ["components[].propsSchema"];
156
152
  var CategoriesResource = class {
157
153
  http;
@@ -218,6 +214,15 @@ var DynamicComponentsResource = class {
218
214
  preserve: CATALOG_PRESERVE
219
215
  });
220
216
  }
217
+ update(id, input) {
218
+ return this.http.request("PATCH", `/dynamic-components/${encodeURIComponent(id)}`, {
219
+ body: input,
220
+ preserve: CATALOG_PRESERVE
221
+ });
222
+ }
223
+ remove(id) {
224
+ return this.http.request("DELETE", `/dynamic-components/${encodeURIComponent(id)}`, { preserve: CATALOG_PRESERVE });
225
+ }
221
226
  };
222
227
  var FilesResource = class {
223
228
  http;
@@ -237,8 +242,7 @@ var FilesResource = class {
237
242
  });
238
243
  }
239
244
  };
240
- //#endregion
241
- //#region src/resources/drafts.ts
245
+ // src/resources/drafts.ts
242
246
  var DraftsResource = class {
243
247
  http;
244
248
  constructor(http) {
@@ -263,8 +267,7 @@ var DraftsResource = class {
263
267
  return this.http.request("DELETE", `/drafts/${id}`);
264
268
  }
265
269
  };
266
- //#endregion
267
- //#region src/resources/posts.ts
270
+ // src/resources/posts.ts
268
271
  const POST_PRESERVE = ["meta"];
269
272
  const POST_LIST_PRESERVE = ["[].meta"];
270
273
  const DRAFT_CORE_KEYS = /* @__PURE__ */ new Set([
@@ -330,6 +333,12 @@ var PostsResource = class {
330
333
  preserve: POST_PRESERVE
331
334
  });
332
335
  }
336
+ getBySlugOnly(slug, options = {}) {
337
+ return this.http.request("GET", `/posts/slug/${encodeURIComponent(slug)}`, {
338
+ query: { lang: options.lang ?? this.http.lang },
339
+ preserve: POST_PRESERVE
340
+ });
341
+ }
333
342
  getPath(slug) {
334
343
  return this.http.request("GET", `/posts/get-url/${encodeURIComponent(slug)}`);
335
344
  }
@@ -374,7 +383,8 @@ var PostsResource = class {
374
383
  refType: "post",
375
384
  refId: id,
376
385
  baseRevisionId: base.id,
377
- data: mergeSnapshot(base, toDraftData(input))
386
+ data: mergeSnapshot(base, toDraftData(input)),
387
+ ...options.publish === false ? {} : { ifInSync: true }
378
388
  });
379
389
  return this.finish(draft, options);
380
390
  }
@@ -388,7 +398,6 @@ var PostsResource = class {
388
398
  branchId: draft.id,
389
399
  revisionId: draft.headRevisionId,
390
400
  expectedPublishedRevisionId: draft.document.publishedRevisionId,
391
- confirmDiverged: draft.relationToPublished === "descendant" || draft.relationToPublished === "diverged",
392
401
  aiResources: options.aiResources,
393
402
  regenerateLockedTranslations: options.regenerateLockedTranslations
394
403
  }, options);
@@ -399,8 +408,7 @@ var PostsResource = class {
399
408
  };
400
409
  }
401
410
  };
402
- //#endregion
403
- //#region src/resources/tasks.ts
411
+ // src/resources/tasks.ts
404
412
  const TERMINAL = /* @__PURE__ */ new Set([
405
413
  "completed",
406
414
  "partial_failed",
@@ -453,8 +461,7 @@ var PublishJobsResource = class {
453
461
  return task;
454
462
  }
455
463
  };
456
- //#endregion
457
- //#region src/resources/users.ts
464
+ // src/resources/users.ts
458
465
  var UsersResource = class {
459
466
  http;
460
467
  constructor(http) {
@@ -531,8 +538,7 @@ var InvitesResource = class {
531
538
  return this.http.request("POST", `/invites/${encodeURIComponent(token)}/accept`, { body: input });
532
539
  }
533
540
  };
534
- //#endregion
535
- //#region src/client.ts
541
+ // src/client.ts
536
542
  var BylineClient = class {
537
543
  http;
538
544
  posts;
@@ -562,5 +568,4 @@ var BylineClient = class {
562
568
  }
563
569
  };
564
570
  const createClient = (options) => new BylineClient(options);
565
- //#endregion
566
571
  export { BylineApiError, BylineClient, HttpCore, camelcaseKeys, createClient, isBylineApiError, toDraftData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-space/byline-api-client",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Typed fetch client for the Byline headless CMS API",
5
5
  "keywords": [
6
6
  "byline",
@@ -44,7 +44,7 @@
44
44
  "access": "public"
45
45
  },
46
46
  "devDependencies": {
47
- "tsdown": "0.22.8",
47
+ "tsdown": "0.23.1",
48
48
  "typescript": "6.0.3",
49
49
  "vitest": "5.0.5"
50
50
  },