@cloudcannon/sdk 0.0.1 → 0.0.3

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.
@@ -0,0 +1,6 @@
1
+ import type CloudCannonClient from '../index.ts';
2
+ export declare class BackupClient {
3
+ #private;
4
+ constructor(uuid: string, client: CloudCannonClient);
5
+ download(): Promise<Response>;
6
+ }
@@ -0,0 +1,15 @@
1
+ export class BackupClient {
2
+ #uuid;
3
+ #client;
4
+ constructor(uuid, client) {
5
+ this.#uuid = uuid;
6
+ this.#client = client;
7
+ }
8
+ async download() {
9
+ const resp = await this.#client.fetch(`/site-archives/${this.#uuid}/download`);
10
+ if (resp.status === 401 || resp.status === 403) {
11
+ throw new Error('Error downloading backup. Permission denied');
12
+ }
13
+ return resp;
14
+ }
15
+ }
@@ -0,0 +1,6 @@
1
+ import type CloudCannonClient from '../index.ts';
2
+ export declare class BuildClient {
3
+ #private;
4
+ constructor(uuid: string, client: CloudCannonClient);
5
+ get(): Promise<Response>;
6
+ }
@@ -0,0 +1,15 @@
1
+ export class BuildClient {
2
+ #uuid;
3
+ #client;
4
+ constructor(uuid, client) {
5
+ this.#uuid = uuid;
6
+ this.#client = client;
7
+ }
8
+ async get() {
9
+ const resp = await this.#client.fetch(`/builds/${this.#uuid}`);
10
+ if (resp.status === 401 || resp.status === 403) {
11
+ throw new Error('Error fetching build. Permission denied');
12
+ }
13
+ return resp;
14
+ }
15
+ }
@@ -0,0 +1,13 @@
1
+ import type CloudCannonClient from '../index.ts';
2
+ import type { EditingSessionFile, EditingSessionFileContribution } from '../index.ts';
3
+ import type { operations } from '../schema.js';
4
+ export type CreateContributionOptions = operations['Editing Session Contributions_Create']['requestBody']['content']['application/json'];
5
+ export type UnlockOptions = operations['Editing Session File_Unlock']['requestBody']['content']['application/json'];
6
+ export declare class EditingSessionFileClient {
7
+ #private;
8
+ constructor(uuid: string, client: CloudCannonClient);
9
+ get(): Promise<EditingSessionFile>;
10
+ getContributions(): Promise<EditingSessionFileContribution[]>;
11
+ createContribution(body: CreateContributionOptions): Promise<EditingSessionFileContribution>;
12
+ unlock(body: UnlockOptions): Promise<EditingSessionFileContribution>;
13
+ }
@@ -0,0 +1,58 @@
1
+ import { ApiError } from "./errors.js";
2
+ export class EditingSessionFileClient {
3
+ #uuid;
4
+ #client;
5
+ constructor(uuid, client) {
6
+ this.#uuid = uuid;
7
+ this.#client = client;
8
+ }
9
+ async get() {
10
+ const resp = await this.#client.fetch(`/editing_session_files/${this.#uuid}`);
11
+ if (resp.status === 403) {
12
+ throw new Error('Error fetching editing session file. Permission denied');
13
+ }
14
+ const file = await resp.json();
15
+ return file;
16
+ }
17
+ async getContributions() {
18
+ const resp = await this.#client.fetch(`/editing_session_files/${this.#uuid}/contributions`);
19
+ if (resp.status === 403) {
20
+ throw new Error('Error fetching editing session file contributions. Permission denied');
21
+ }
22
+ const contributions = await resp.json();
23
+ return contributions;
24
+ }
25
+ async createContribution(body) {
26
+ const resp = await this.#client.fetch(`/editing_session_files/${this.#uuid}/contributions`, {
27
+ method: 'POST',
28
+ body,
29
+ });
30
+ if (resp.status === 401 || resp.status === 403) {
31
+ throw new Error('Error creating editing session file contribution. Permission denied');
32
+ }
33
+ if (resp.status === 422) {
34
+ const errorResp = await resp.json();
35
+ throw new ApiError('Error creating editing session file contribution. Invalid request', errorResp.errors, `/editing_session_files/${this.#uuid}/contributions`, { method: 'POST', body }, resp.status);
36
+ }
37
+ const contribution = await resp.json();
38
+ return contribution;
39
+ }
40
+ async unlock(body) {
41
+ const resp = await this.#client.fetch(`/editing_session_files/${this.#uuid}/unlock`, {
42
+ method: 'PUT',
43
+ body,
44
+ });
45
+ if (resp.status === 403) {
46
+ throw new Error('Error unlocking editing session file. Permission denied');
47
+ }
48
+ if (resp.status === 404) {
49
+ throw new Error('Error unlocking editing session file. File not found');
50
+ }
51
+ if (resp.status === 422) {
52
+ const errorResp = await resp.json();
53
+ throw new ApiError('Error unlocking editing session file. Invalid request', errorResp.errors, `/editing_session_files/${this.#uuid}/unlock`, { method: 'PUT', body }, resp.status);
54
+ }
55
+ const contribution = await resp.json();
56
+ return contribution;
57
+ }
58
+ }
@@ -0,0 +1,14 @@
1
+ import type CloudCannonClient from '../index.ts';
2
+ import type { EditingSession, EditingSessionFile } from '../index.ts';
3
+ import type { operations } from '../schema.js';
4
+ export type CreateEditingSessionFileOptions = operations['Editing Session Files_Create']['requestBody']['content']['application/json'];
5
+ export type CommitEditingSessionOptions = operations['Editing Session_Commit']['requestBody']['content']['application/json'];
6
+ export type CommitEditingSessionResponse = operations['Editing Session_Commit']['responses']['200']['content']['application/json'];
7
+ export declare class EditingSessionClient {
8
+ #private;
9
+ constructor(uuid: string, client: CloudCannonClient);
10
+ get(): Promise<EditingSession>;
11
+ getFiles(): Promise<EditingSessionFile[]>;
12
+ createFile(body: CreateEditingSessionFileOptions): Promise<EditingSessionFile>;
13
+ commit(): Promise<CommitEditingSessionResponse>;
14
+ }
@@ -0,0 +1,54 @@
1
+ import { ApiError } from "./errors.js";
2
+ export class EditingSessionClient {
3
+ #uuid;
4
+ #client;
5
+ constructor(uuid, client) {
6
+ this.#uuid = uuid;
7
+ this.#client = client;
8
+ }
9
+ async get() {
10
+ const resp = await this.#client.fetch(`/editing_sessions/${this.#uuid}`);
11
+ if (resp.status === 403) {
12
+ throw new Error('Error fetching editing session. Permission denied');
13
+ }
14
+ const editingSession = await resp.json();
15
+ return editingSession;
16
+ }
17
+ async getFiles() {
18
+ const resp = await this.#client.fetch(`/editing_sessions/${this.#uuid}/files`);
19
+ if (resp.status === 403) {
20
+ throw new Error('Error fetching editing session files. Permission denied');
21
+ }
22
+ const files = await resp.json();
23
+ return files;
24
+ }
25
+ async createFile(body) {
26
+ const resp = await this.#client.fetch(`/editing_sessions/${this.#uuid}/files`, {
27
+ method: 'POST',
28
+ body,
29
+ });
30
+ if (resp.status === 403) {
31
+ throw new Error('Error creating editing session file. Permission denied');
32
+ }
33
+ if (resp.status === 422) {
34
+ const errorResp = await resp.json();
35
+ throw new ApiError('Error creating editing session file. Invalid request', errorResp.errors, `/editing_sessions/${this.#uuid}/files`, { method: 'POST', body }, resp.status);
36
+ }
37
+ const file = await resp.json();
38
+ return file;
39
+ }
40
+ async commit() {
41
+ const resp = await this.#client.fetch(`/editing_sessions/${this.#uuid}/commit`, {
42
+ method: 'POST',
43
+ });
44
+ if (resp.status === 403) {
45
+ throw new Error('Error committing editing session. Permission denied');
46
+ }
47
+ if (resp.status === 422) {
48
+ const errorResp = await resp.json();
49
+ throw new ApiError('Error committing editing session. Invalid request', errorResp.errors, `/editing_sessions/${this.#uuid}/commit`, { method: 'POST' }, resp.status);
50
+ }
51
+ const result = await resp.json();
52
+ return result;
53
+ }
54
+ }
@@ -0,0 +1,28 @@
1
+ export type PaginationOptions = {
2
+ page?: number;
3
+ items?: number;
4
+ };
5
+ export type SortingOptions<Op> = Op extends {
6
+ parameters: {
7
+ query?: infer Q;
8
+ };
9
+ } ? Pick<NonNullable<Q>, Extract<keyof NonNullable<Q>, 'sort_attribute' | 'sort_direction'>> : never;
10
+ export type FilterOptions<Op> = Op extends {
11
+ parameters: {
12
+ query?: infer Q;
13
+ };
14
+ } ? {
15
+ filters?: Omit<NonNullable<Q>, 'page' | 'items' | 'sort_attribute' | 'sort_direction'>;
16
+ } : never;
17
+ export type PaginatedResponse<T> = {
18
+ items: T[];
19
+ current_page?: number;
20
+ total_items?: number;
21
+ total_pages?: number;
22
+ };
23
+ export declare function buildQuery(options?: PaginationOptions & {
24
+ sort_attribute?: string;
25
+ sort_direction?: 'ASC' | 'DESC';
26
+ filters?: Record<string, unknown>;
27
+ }): string;
28
+ export declare function paginatedResponse<T>(items: T[], headers: Headers): PaginatedResponse<T>;
@@ -0,0 +1,34 @@
1
+ export function buildQuery(options = {}) {
2
+ const params = new URLSearchParams();
3
+ if (options.page !== undefined)
4
+ params.set('page', String(options.page));
5
+ if (options.items !== undefined)
6
+ params.set('item', String(options.items));
7
+ if (options.sort_attribute !== undefined)
8
+ params.set('sort_attribute', options.sort_attribute);
9
+ if (options.sort_direction !== undefined)
10
+ params.set('sort_direction', options.sort_direction);
11
+ if (options.filters) {
12
+ for (const [key, value] of Object.entries(options.filters)) {
13
+ if (value !== undefined) {
14
+ params.set(key, String(value));
15
+ }
16
+ }
17
+ }
18
+ return params.toString();
19
+ }
20
+ export function paginatedResponse(items, headers) {
21
+ const parse = (name) => {
22
+ const value = headers.get(name);
23
+ if (value === null)
24
+ return undefined;
25
+ const parsed = Number(value);
26
+ return Number.isFinite(parsed) ? parsed : undefined;
27
+ };
28
+ return {
29
+ items,
30
+ current_page: parse('current_page'),
31
+ total_items: parse('total_items'),
32
+ total_pages: parse('total_pages'),
33
+ };
34
+ }
@@ -1,7 +1,9 @@
1
1
  import type CloudCannonClient from '../index.ts';
2
2
  import type { FormSubmission } from '../index.ts';
3
+ import type { operations } from '../schema.js';
4
+ import { type FilterOptions, type PaginatedResponse, type PaginationOptions, type SortingOptions } from './helpers/query.ts';
3
5
  export declare class InboxClient {
4
6
  #private;
5
7
  constructor(uuid: string, client: CloudCannonClient);
6
- getSubmissions(): Promise<FormSubmission[]>;
8
+ getSubmissions(options?: PaginationOptions & SortingOptions<operations['Inbox Form Hooks_Index']> & FilterOptions<operations['Inbox Form Hooks_Index']>): Promise<PaginatedResponse<FormSubmission>>;
7
9
  }
package/dist/src/inbox.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { buildQuery, paginatedResponse, } from "./helpers/query.js";
1
2
  export class InboxClient {
2
3
  #uuid;
3
4
  #client;
@@ -5,12 +6,13 @@ export class InboxClient {
5
6
  this.#uuid = uuid;
6
7
  this.#client = client;
7
8
  }
8
- async getSubmissions() {
9
- const resp = await this.#client.fetch(`/inboxes/${this.#uuid}/form-hooks`);
9
+ async getSubmissions(options = {}) {
10
+ const query = buildQuery(options);
11
+ const resp = await this.#client.fetch(`/inboxes/${this.#uuid}/form-hooks?${query}`);
10
12
  if (resp.status === 401 || resp.status === 403) {
11
13
  throw new Error('Error fetching submissions. Permission denied');
12
14
  }
13
15
  const submissions = await resp.json();
14
- return submissions;
16
+ return paginatedResponse(submissions, resp.headers);
15
17
  }
16
18
  }
package/dist/src/org.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type CloudCannonClient from '../index.ts';
2
2
  import type { Dam, Inbox, Org, Provider, ProviderDetails, Site } from '../index.ts';
3
3
  import type { operations } from '../schema.js';
4
+ import { type FilterOptions, type PaginatedResponse, type PaginationOptions, type SortingOptions } from './helpers/query.ts';
4
5
  export interface ConnectSiteOptions extends ProviderDetails {
5
6
  folder?: string;
6
7
  }
@@ -10,13 +11,13 @@ export type CreateDamOptions = operations['DAMs_Create']['requestBody']['content
10
11
  export declare class OrgClient {
11
12
  #private;
12
13
  constructor(uuid: string, client: CloudCannonClient);
13
- sites(): Promise<Site[]>;
14
+ sites(options?: PaginationOptions & SortingOptions<operations['Sites_Index']> & FilterOptions<operations['Sites_Index']>): Promise<PaginatedResponse<Site>>;
14
15
  createSite(name: string, stableDomain?: string): Promise<Site>;
15
16
  connectSite(name: string, providerDetails: ConnectSiteOptions, stableDomain?: string): Promise<Site>;
16
17
  get(): Promise<Org>;
17
- getInboxes(): Promise<Inbox[]>;
18
+ getInboxes(options?: PaginationOptions & SortingOptions<operations['Organization Inboxes_Index']> & FilterOptions<operations['Organization Inboxes_Index']>): Promise<PaginatedResponse<Inbox>>;
18
19
  createInbox(body: CreateInboxOptions): Promise<Inbox>;
19
- getDams(): Promise<Dam[]>;
20
+ getDams(options?: PaginationOptions & SortingOptions<operations['DAMs_Index']> & FilterOptions<operations['DAMs_Index']>): Promise<PaginatedResponse<Dam>>;
20
21
  createDam(body: CreateDamOptions): Promise<Dam>;
21
22
  getRepositories(provider: Provider): Promise<Repository[]>;
22
23
  }
package/dist/src/org.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ApiError } from "./errors.js";
2
+ import { buildQuery, paginatedResponse, } from "./helpers/query.js";
2
3
  export class OrgClient {
3
4
  #uuid;
4
5
  #client;
@@ -6,13 +7,14 @@ export class OrgClient {
6
7
  this.#uuid = uuid;
7
8
  this.#client = client;
8
9
  }
9
- async sites() {
10
- const resp = await this.#client.fetch(`/orgs/${this.#uuid}/sites`);
10
+ async sites(options = {}) {
11
+ const query = buildQuery(options);
12
+ const resp = await this.#client.fetch(`/orgs/${this.#uuid}/sites?${query}`);
11
13
  if (resp.status === 401 || resp.status === 403) {
12
14
  throw new Error('Error fetching sites. Permission denied');
13
15
  }
14
16
  const sites = await resp.json();
15
- return sites;
17
+ return paginatedResponse(sites, resp.headers);
16
18
  }
17
19
  async createSite(name, stableDomain) {
18
20
  const resp = await this.#client.fetch(`/orgs/${this.#uuid}/sites`, {
@@ -63,13 +65,14 @@ export class OrgClient {
63
65
  const org = await resp.json();
64
66
  return org;
65
67
  }
66
- async getInboxes() {
67
- const resp = await this.#client.fetch(`/orgs/${this.#uuid}/inboxes`);
68
+ async getInboxes(options = {}) {
69
+ const query = buildQuery(options);
70
+ const resp = await this.#client.fetch(`/orgs/${this.#uuid}/inboxes?${query}`);
68
71
  if (resp.status === 403) {
69
72
  throw new Error('Error fetching inboxes. Permission denied');
70
73
  }
71
74
  const inboxes = await resp.json();
72
- return inboxes;
75
+ return paginatedResponse(inboxes, resp.headers);
73
76
  }
74
77
  async createInbox(body) {
75
78
  const resp = await this.#client.fetch(`/orgs/${this.#uuid}/inboxes`, {
@@ -86,10 +89,11 @@ export class OrgClient {
86
89
  const inbox = await resp.json();
87
90
  return inbox;
88
91
  }
89
- async getDams() {
90
- const resp = await this.#client.fetch(`/orgs/${this.#uuid}/dams`);
92
+ async getDams(options = {}) {
93
+ const query = buildQuery(options);
94
+ const resp = await this.#client.fetch(`/orgs/${this.#uuid}/dams?${query}`);
91
95
  const dams = await resp.json();
92
- return dams;
96
+ return paginatedResponse(dams, resp.headers);
93
97
  }
94
98
  async createDam(body) {
95
99
  const resp = await this.#client.fetch(`/orgs/${this.#uuid}/dams`, {
@@ -1,6 +1,7 @@
1
1
  import type CloudCannonClient from '../index.ts';
2
- import type { Build, ProviderDetails, Site, SiteDam, SiteInbox, SiteScan, Sync } from '../index.ts';
2
+ import type { Backup, Build, EditingSession, ProviderDetails, Site, SiteDam, SiteInbox, SiteScan, Sync } from '../index.ts';
3
3
  import type { operations } from '../schema.js';
4
+ import { type FilterOptions, type PaginatedResponse, type PaginationOptions, type SortingOptions } from './helpers/query.ts';
4
5
  export type BuildConfiguration = Partial<Omit<operations['Sites_UpdateBuild']['requestBody']['content']['application/json'], 'build_configuration'>> & {
5
6
  compile?: {
6
7
  install_command?: string;
@@ -22,8 +23,14 @@ export type BuildConfiguration = Partial<Omit<operations['Sites_UpdateBuild']['r
22
23
  };
23
24
  export type UpdateSiteOptions = operations['Sites_Update']['requestBody']['content']['application/json'];
24
25
  export type CopySiteOptions = operations['Sites_Copy']['requestBody']['content']['application/json'];
26
+ export type CreateBackupOptions = operations['Backups_Create']['requestBody']['content']['application/json'];
25
27
  export type ConnectInboxOptions = operations['Site Inboxes_Create']['requestBody']['content']['application/json'];
26
28
  export type ConnectDamOptions = operations['Dams_Create']['requestBody']['content']['application/json'];
29
+ export type FileListing = operations['Files_Index']['responses']['200']['content']['application/json'][number];
30
+ export type UploadFileOptions = {
31
+ type?: string;
32
+ overwriteExistingFile?: boolean;
33
+ };
27
34
  export declare class SiteClient {
28
35
  #private;
29
36
  constructor(uuid: string, client: CloudCannonClient);
@@ -32,10 +39,15 @@ export declare class SiteClient {
32
39
  delete(): Promise<void>;
33
40
  copy(body: CopySiteOptions): Promise<Site>;
34
41
  updateBuildConfig(options: BuildConfiguration): Promise<Site>;
35
- getBuilds(): Promise<Build[]>;
42
+ getBuilds(options?: PaginationOptions & SortingOptions<operations['Builds_Index']> & FilterOptions<operations['Builds_Index']>): Promise<PaginatedResponse<Build>>;
36
43
  rebuild(): Promise<void>;
44
+ listBackups(options?: PaginationOptions & SortingOptions<operations['Backups_Index']> & FilterOptions<operations['Backups_Index']>): Promise<PaginatedResponse<Backup>>;
45
+ createBackup(body?: CreateBackupOptions): Promise<{
46
+ socket_message_id?: string;
47
+ }>;
48
+ listFiles(): Promise<FileListing[]>;
37
49
  getFile(path: string): Promise<Response>;
38
- getSyncs(): Promise<Sync[]>;
50
+ getSyncs(options?: PaginationOptions & SortingOptions<operations['Syncs_Index']> & FilterOptions<operations['Syncs_Index']>): Promise<PaginatedResponse<Sync>>;
39
51
  getScan(): Promise<SiteScan>;
40
52
  getScreenshotHashes(): Promise<Record<string, string>>;
41
53
  getScreenshot(device: 'desktop' | 'mobile', path: string): Promise<Response>;
@@ -48,4 +60,8 @@ export declare class SiteClient {
48
60
  connectInbox(body: ConnectInboxOptions): Promise<SiteInbox>;
49
61
  getDamConnections(): Promise<SiteDam[]>;
50
62
  connectDam(body: ConnectDamOptions): Promise<SiteDam>;
63
+ getEditingSessions(): Promise<EditingSession[]>;
64
+ createEditingSession(): Promise<EditingSession>;
65
+ getLatestEditingSession(): Promise<EditingSession>;
66
+ uploadFile(path: string, content: BlobPart, options?: UploadFileOptions): Promise<void>;
51
67
  }
package/dist/src/site.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ApiError } from "./errors.js";
2
+ import { buildQuery, paginatedResponse, } from "./helpers/query.js";
2
3
  export class SiteClient {
3
4
  #uuid;
4
5
  #client;
@@ -70,13 +71,14 @@ export class SiteClient {
70
71
  const site = await resp.json();
71
72
  return site;
72
73
  }
73
- async getBuilds() {
74
- const resp = await this.#client.fetch(`/sites/${this.#uuid}/builds`);
74
+ async getBuilds(options = {}) {
75
+ const query = buildQuery(options);
76
+ const resp = await this.#client.fetch(`/sites/${this.#uuid}/builds?${query}`);
75
77
  if (resp.status === 401 || resp.status === 403) {
76
78
  throw new Error('Error fetching builds. Permission denied');
77
79
  }
78
80
  const builds = await resp.json();
79
- return builds;
81
+ return paginatedResponse(builds, resp.headers);
80
82
  }
81
83
  async rebuild() {
82
84
  const resp = await this.#client.fetch(`/sites/${this.#uuid}/builds`, {
@@ -86,6 +88,42 @@ export class SiteClient {
86
88
  throw new Error('Error creating build. Permission denied');
87
89
  }
88
90
  }
91
+ async listBackups(options = {}) {
92
+ const query = buildQuery(options);
93
+ const resp = await this.#client.fetch(`/sites/${this.#uuid}/archives?${query}`);
94
+ if (resp.status === 401) {
95
+ throw new Error('Error fetching backups. Permission denied');
96
+ }
97
+ const items = await resp.json();
98
+ return paginatedResponse(items, resp.headers);
99
+ }
100
+ async createBackup(body = {}) {
101
+ const resp = await this.#client.fetch(`/sites/${this.#uuid}/archives`, {
102
+ method: 'POST',
103
+ body,
104
+ });
105
+ if (resp.status === 401) {
106
+ throw new Error('Error creating backup. Permission denied');
107
+ }
108
+ if (resp.status === 422) {
109
+ const errorResp = await resp.json();
110
+ throw new ApiError('Error creating backup. Invalid request', errorResp.errors, `/sites/${this.#uuid}/archives`, { method: 'POST', body }, resp.status);
111
+ }
112
+ const result = await resp.json();
113
+ return result;
114
+ }
115
+ async listFiles() {
116
+ const resp = await this.#client.fetch(`/sites/${this.#uuid}/files`);
117
+ if (resp.status === 401) {
118
+ throw new Error('Error fetching files. Permission denied');
119
+ }
120
+ if (resp.status === 422) {
121
+ const errorResp = await resp.json();
122
+ throw new ApiError('Error fetching files. Invalid request', errorResp.errors, `/sites/${this.#uuid}/files`, {}, resp.status);
123
+ }
124
+ const files = await resp.json();
125
+ return files;
126
+ }
89
127
  async getFile(path) {
90
128
  const resp = await this.#client.fetch(`/sites/${this.#uuid}/files/${encodeURIComponent(path)}`);
91
129
  if (resp.status === 401 || resp.status === 403) {
@@ -100,13 +138,14 @@ export class SiteClient {
100
138
  }
101
139
  return resp;
102
140
  }
103
- async getSyncs() {
104
- const resp = await this.#client.fetch(`/sites/${this.#uuid}/syncs`);
141
+ async getSyncs(options = {}) {
142
+ const query = buildQuery(options);
143
+ const resp = await this.#client.fetch(`/sites/${this.#uuid}/syncs?${query}`);
105
144
  if (resp.status === 401) {
106
145
  throw new Error('Error fetching syncs. Permission denied');
107
146
  }
108
147
  const syncs = await resp.json();
109
- return syncs;
148
+ return paginatedResponse(syncs, resp.headers);
110
149
  }
111
150
  async getScan() {
112
151
  const resp = await this.#client.fetch(`/sites/${this.#uuid}/scans`);
@@ -251,4 +290,83 @@ export class SiteClient {
251
290
  const dam = await resp.json();
252
291
  return dam;
253
292
  }
293
+ async getEditingSessions() {
294
+ const resp = await this.#client.fetch(`/sites/${this.#uuid}/editing_sessions`);
295
+ if (resp.status === 403) {
296
+ throw new Error('Error fetching editing sessions. Permission denied');
297
+ }
298
+ const editingSessions = await resp.json();
299
+ return editingSessions;
300
+ }
301
+ async createEditingSession() {
302
+ const resp = await this.#client.fetch(`/sites/${this.#uuid}/editing_sessions`, {
303
+ method: 'POST',
304
+ });
305
+ if (resp.status === 401 || resp.status === 403) {
306
+ throw new Error('Error creating editing session. Permission denied');
307
+ }
308
+ if (resp.status === 422) {
309
+ const errorResp = await resp.json();
310
+ throw new ApiError('Error creating editing session. Invalid request', errorResp.errors, `/sites/${this.#uuid}/editing_sessions`, { method: 'POST' }, resp.status);
311
+ }
312
+ const editingSession = await resp.json();
313
+ return editingSession;
314
+ }
315
+ async getLatestEditingSession() {
316
+ const resp = await this.#client.fetch(`/sites/${this.#uuid}/editing_sessions/latest`);
317
+ if (resp.status === 403) {
318
+ throw new Error('Error fetching latest editing session. Permission denied');
319
+ }
320
+ const editingSession = await resp.json();
321
+ return editingSession;
322
+ }
323
+ async uploadFile(path, content, options = {}) {
324
+ if (!path.startsWith('/')) {
325
+ path = `/${path}`;
326
+ }
327
+ const files = await this.listFiles();
328
+ const existingFile = files.find((file) => file.sitePath === path);
329
+ if (existingFile && !options.overwriteExistingFile) {
330
+ throw new Error('File already exists and overwriteExistingFile is not set');
331
+ }
332
+ const uploadData = await this.#client.getUploadData();
333
+ const editingSession = await this.createEditingSession();
334
+ const file = await this.#client.editingSession(editingSession.uuid).createFile({
335
+ path,
336
+ source_path: existingFile ? path : undefined,
337
+ edit_type: 'update',
338
+ });
339
+ const contributions = await this.#client.editingSessionFile(file.uuid).getContributions();
340
+ contributions.sort((a, b) => a.updated_at.localeCompare(b.updated_at));
341
+ const latestContribution = contributions.at(-1);
342
+ if (latestContribution && !options.overwriteExistingFile) {
343
+ throw new Error('File already exists and overwriteExistingFile is not set');
344
+ }
345
+ const s3Key = `${uploadData.prefix}/${Date.now()}${path}`;
346
+ const formData = new FormData();
347
+ formData.append('key', s3Key);
348
+ Object.keys(uploadData.fields).forEach((field) => {
349
+ if (field !== 'key') {
350
+ formData.append(field, uploadData.fields[field]);
351
+ }
352
+ });
353
+ formData.append('file', new Blob([content], { type: options.type ?? 'text/plain' }));
354
+ const uploadResp = await fetch(uploadData.url, {
355
+ method: 'POST',
356
+ body: formData,
357
+ });
358
+ const etag = uploadResp.headers.get('ETag');
359
+ if (!etag) {
360
+ throw new Error('ETag not found in upload response');
361
+ }
362
+ const contentHash = etag.slice(1, -1);
363
+ await this.#client.editingSessionFile(file.uuid).createContribution({
364
+ s3_key: s3Key,
365
+ content_hash: contentHash,
366
+ previous_content_hash: latestContribution?.content_hash ?? existingFile?.md5,
367
+ });
368
+ await this.#client.editingSessionFile(file.uuid).unlock({
369
+ previous_content_hash: contentHash,
370
+ });
371
+ }
254
372
  }
@@ -0,0 +1,6 @@
1
+ import type CloudCannonClient from '../index.ts';
2
+ export declare class SyncClient {
3
+ #private;
4
+ constructor(uuid: string, client: CloudCannonClient);
5
+ get(): Promise<Response>;
6
+ }
@@ -0,0 +1,15 @@
1
+ export class SyncClient {
2
+ #uuid;
3
+ #client;
4
+ constructor(uuid, client) {
5
+ this.#uuid = uuid;
6
+ this.#client = client;
7
+ }
8
+ async get() {
9
+ const resp = await this.#client.fetch(`/syncs/${this.#uuid}`);
10
+ if (resp.status === 401 || resp.status === 403) {
11
+ throw new Error('Error fetching sync. Permission denied');
12
+ }
13
+ return resp;
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcannon/sdk",
3
3
  "type": "module",
4
- "version": "0.0.1",
4
+ "version": "0.0.3",
5
5
  "description": "REST API client for the CloudCannon CMS.",
6
6
  "keywords": [
7
7
  "cloudcannon",
@@ -43,7 +43,8 @@
43
43
  "test:watch": "node --test --watch",
44
44
  "test:coverage": "node --test --test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info",
45
45
  "lint": "biome check && tsc --noEmit",
46
- "lint:fix": "biome check --fix"
46
+ "lint:fix": "biome check --fix",
47
+ "generate-schema": "npx openapi-typescript $1 -o ./schema.ts"
47
48
  },
48
49
  "author": "CloudCannon <support@cloudcannon.com>",
49
50
  "license": "ISC",