syncsnap 1.0.0 → 1.0.2

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
@@ -4,4 +4,4 @@ Syncsnap server SDK for Node and Next.js — API client and route handlers for f
4
4
 
5
5
  ## Docs
6
6
 
7
- **[Documentation →](https://docs.syncsnap.com)**
7
+ **[Documentation →](https://docs.syncsnap.xyz)**
@@ -0,0 +1,99 @@
1
+ // src/client.ts
2
+ var SYNCSNAP_API_BASE_URL = "https://api.syncsnap.xyz/api";
3
+ var SyncsnapRateLimitError = class _SyncsnapRateLimitError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.statusCode = 429;
7
+ this.name = "SyncsnapRateLimitError";
8
+ Object.setPrototypeOf(this, _SyncsnapRateLimitError.prototype);
9
+ }
10
+ };
11
+ var SyncsnapServer = class {
12
+ constructor(apiVersion = "v1") {
13
+ this.baseUrl = `${SYNCSNAP_API_BASE_URL}/${apiVersion}`;
14
+ this.token = process.env.SYNCSNAP_TOKEN;
15
+ this.fetcher = fetch;
16
+ }
17
+ authHeaders() {
18
+ return {
19
+ "Content-Type": "application/json",
20
+ "X-API-KEY": this.token
21
+ };
22
+ }
23
+ async requestJson(url, init) {
24
+ const res = await this.fetcher(url, init);
25
+ const data = await res.json().catch(() => ({}));
26
+ if (!res.ok) {
27
+ const message = typeof data === "object" && data && "error" in data && data.error ? String(data.error) : `Syncsnap request failed (${res.status})`;
28
+ if (res.status === 429) {
29
+ throw new SyncsnapRateLimitError(message);
30
+ }
31
+ throw new Error(message);
32
+ }
33
+ return data;
34
+ }
35
+ async createJob() {
36
+ const url = `${this.baseUrl}/jobs`;
37
+ return this.requestJson(url, {
38
+ method: "POST",
39
+ headers: this.authHeaders()
40
+ });
41
+ }
42
+ async getJob(jobId) {
43
+ const url = `${this.baseUrl}/jobs/${encodeURIComponent(jobId)}`;
44
+ return this.requestJson(url, {
45
+ method: "GET",
46
+ headers: this.authHeaders()
47
+ });
48
+ }
49
+ async getUploadUrl(jobId, options) {
50
+ const url = `${this.baseUrl}/jobs/${encodeURIComponent(
51
+ jobId
52
+ )}/presigned-upload-url`;
53
+ return this.requestJson(url, {
54
+ method: "POST",
55
+ headers: this.authHeaders(),
56
+ body: JSON.stringify({
57
+ file_name: options.fileName,
58
+ expiration: options.expirationMinutes
59
+ })
60
+ });
61
+ }
62
+ async getDownloadUrl(jobId, options) {
63
+ const url = `${this.baseUrl}/jobs/${encodeURIComponent(
64
+ jobId
65
+ )}/presigned-download-url`;
66
+ return this.requestJson(url, {
67
+ method: "POST",
68
+ headers: this.authHeaders(),
69
+ body: JSON.stringify({
70
+ expiration: options?.expirationMinutes
71
+ })
72
+ });
73
+ }
74
+ async waitForJobCompletion(jobId, options = {}) {
75
+ const intervalMs = options.intervalMs ?? 2e3;
76
+ const timeoutMs = options.timeoutMs ?? 12e4;
77
+ const startedAt = Date.now();
78
+ while (true) {
79
+ if (options.signal?.aborted) {
80
+ throw new Error("Polling aborted");
81
+ }
82
+ if (Date.now() - startedAt > timeoutMs) {
83
+ throw new Error("Polling timed out");
84
+ }
85
+ const job = await this.getJob(jobId);
86
+ options.onPoll?.(job);
87
+ if (job.status === "completed" || job.status === "failed") {
88
+ return job;
89
+ }
90
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
91
+ }
92
+ }
93
+ };
94
+
95
+ export {
96
+ SyncsnapRateLimitError,
97
+ SyncsnapServer
98
+ };
99
+ //# sourceMappingURL=chunk-SUNHYVAG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type {\n CreateJobResponse,\n Job,\n PresignedUrlResponse,\n WaitForJobOptions,\n} from './types';\n\nconst SYNCSNAP_API_BASE_URL = 'https://api.syncsnap.xyz/api';\n\n/** Thrown when the Syncsnap API returns 429 Too Many Requests (rate limit exceeded). */\nexport class SyncsnapRateLimitError extends Error {\n readonly statusCode = 429;\n\n constructor(message: string) {\n super(message);\n this.name = 'SyncsnapRateLimitError';\n Object.setPrototypeOf(this, SyncsnapRateLimitError.prototype);\n }\n}\n\nexport class SyncsnapServer {\n private readonly baseUrl: string;\n private readonly token: string;\n private readonly fetcher: typeof fetch;\n\n constructor(apiVersion: string = 'v1') {\n this.baseUrl = `${SYNCSNAP_API_BASE_URL}/${apiVersion}`;\n this.token = process.env.SYNCSNAP_TOKEN!;\n this.fetcher = fetch;\n }\n\n private authHeaders(): HeadersInit {\n return {\n 'Content-Type': 'application/json',\n 'X-API-KEY': this.token,\n };\n }\n\n private async requestJson<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await this.fetcher(url, init);\n const data = (await res.json().catch(() => ({}))) as T & {\n error?: string;\n };\n\n if (!res.ok) {\n const message =\n typeof data === 'object' && data && 'error' in data && data.error\n ? String(data.error)\n : `Syncsnap request failed (${res.status})`;\n if (res.status === 429) {\n throw new SyncsnapRateLimitError(message);\n }\n throw new Error(message);\n }\n\n return data as T;\n }\n\n async createJob(): Promise<CreateJobResponse> {\n const url = `${this.baseUrl}/jobs`;\n return this.requestJson<CreateJobResponse>(url, {\n method: 'POST',\n headers: this.authHeaders(),\n });\n }\n\n async getJob(jobId: string): Promise<Job> {\n const url = `${this.baseUrl}/jobs/${encodeURIComponent(jobId)}`;\n return this.requestJson<Job>(url, {\n method: 'GET',\n headers: this.authHeaders(),\n });\n }\n\n async getUploadUrl(\n jobId: string,\n options: { fileName: string; expirationMinutes?: number }\n ): Promise<PresignedUrlResponse> {\n const url = `${this.baseUrl}/jobs/${encodeURIComponent(\n jobId\n )}/presigned-upload-url`;\n return this.requestJson<PresignedUrlResponse>(url, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n file_name: options.fileName,\n expiration: options.expirationMinutes,\n }),\n });\n }\n\n async getDownloadUrl(\n jobId: string,\n options?: { expirationMinutes?: number }\n ): Promise<PresignedUrlResponse> {\n const url = `${this.baseUrl}/jobs/${encodeURIComponent(\n jobId\n )}/presigned-download-url`;\n return this.requestJson<PresignedUrlResponse>(url, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n expiration: options?.expirationMinutes,\n }),\n });\n }\n\n async waitForJobCompletion(\n jobId: string,\n options: WaitForJobOptions = {}\n ): Promise<Job> {\n const intervalMs = options.intervalMs ?? 2000;\n const timeoutMs = options.timeoutMs ?? 120000;\n const startedAt = Date.now();\n\n while (true) {\n if (options.signal?.aborted) {\n throw new Error('Polling aborted');\n }\n if (Date.now() - startedAt > timeoutMs) {\n throw new Error('Polling timed out');\n }\n\n const job = await this.getJob(jobId);\n options.onPoll?.(job);\n\n if (job.status === 'completed' || job.status === 'failed') {\n return job;\n }\n\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -38,6 +38,11 @@ interface WaitCompletionResponse<T = unknown> {
38
38
  result?: T;
39
39
  }
40
40
 
41
+ /** Thrown when the Syncsnap API returns 429 Too Many Requests (rate limit exceeded). */
42
+ declare class SyncsnapRateLimitError extends Error {
43
+ readonly statusCode = 429;
44
+ constructor(message: string);
45
+ }
41
46
  declare class SyncsnapServer {
42
47
  private readonly baseUrl;
43
48
  private readonly token;
@@ -57,4 +62,4 @@ declare class SyncsnapServer {
57
62
  waitForJobCompletion(jobId: string, options?: WaitForJobOptions): Promise<Job>;
58
63
  }
59
64
 
60
- export { type CreateJobResponse, type DownloadUrlResponse, type Job, type JobStatus, type PresignedUrlResponse, SyncsnapServer, type WaitCompletionResponse, type WaitForJobOptions };
65
+ export { type CreateJobResponse, type DownloadUrlResponse, type Job, type JobStatus, type PresignedUrlResponse, SyncsnapRateLimitError, SyncsnapServer, type WaitCompletionResponse, type WaitForJobOptions };
package/dist/index.js CHANGED
@@ -1,86 +1,9 @@
1
- // src/client.ts
2
- var SYNCSNAP_API_BASE_URL = "https://api.syncsnap.xyz/api";
3
- var SyncsnapServer = class {
4
- constructor(apiVersion = "v1") {
5
- this.baseUrl = `${SYNCSNAP_API_BASE_URL}/${apiVersion}`;
6
- this.token = process.env.SYNCSNAP_TOKEN;
7
- this.fetcher = fetch;
8
- }
9
- authHeaders() {
10
- return {
11
- "Content-Type": "application/json",
12
- "X-API-KEY": this.token
13
- };
14
- }
15
- async requestJson(url, init) {
16
- const res = await this.fetcher(url, init);
17
- const data = await res.json().catch(() => ({}));
18
- if (!res.ok) {
19
- const message = typeof data === "object" && data && "error" in data && data.error ? String(data.error) : `Syncsnap request failed (${res.status})`;
20
- throw new Error(message);
21
- }
22
- return data;
23
- }
24
- async createJob() {
25
- const url = `${this.baseUrl}/jobs`;
26
- return this.requestJson(url, {
27
- method: "POST",
28
- headers: this.authHeaders()
29
- });
30
- }
31
- async getJob(jobId) {
32
- const url = `${this.baseUrl}/jobs/${encodeURIComponent(jobId)}`;
33
- return this.requestJson(url, {
34
- method: "GET",
35
- headers: this.authHeaders()
36
- });
37
- }
38
- async getUploadUrl(jobId, options) {
39
- const url = `${this.baseUrl}/jobs/${encodeURIComponent(
40
- jobId
41
- )}/presigned-upload-url`;
42
- return this.requestJson(url, {
43
- method: "POST",
44
- headers: this.authHeaders(),
45
- body: JSON.stringify({
46
- file_name: options.fileName,
47
- expiration: options.expirationMinutes
48
- })
49
- });
50
- }
51
- async getDownloadUrl(jobId, options) {
52
- const url = `${this.baseUrl}/jobs/${encodeURIComponent(
53
- jobId
54
- )}/presigned-download-url`;
55
- return this.requestJson(url, {
56
- method: "POST",
57
- headers: this.authHeaders(),
58
- body: JSON.stringify({
59
- expiration: options?.expirationMinutes
60
- })
61
- });
62
- }
63
- async waitForJobCompletion(jobId, options = {}) {
64
- const intervalMs = options.intervalMs ?? 2e3;
65
- const timeoutMs = options.timeoutMs ?? 12e4;
66
- const startedAt = Date.now();
67
- while (true) {
68
- if (options.signal?.aborted) {
69
- throw new Error("Polling aborted");
70
- }
71
- if (Date.now() - startedAt > timeoutMs) {
72
- throw new Error("Polling timed out");
73
- }
74
- const job = await this.getJob(jobId);
75
- options.onPoll?.(job);
76
- if (job.status === "completed" || job.status === "failed") {
77
- return job;
78
- }
79
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
80
- }
81
- }
82
- };
1
+ import {
2
+ SyncsnapRateLimitError,
3
+ SyncsnapServer
4
+ } from "./chunk-SUNHYVAG.js";
83
5
  export {
6
+ SyncsnapRateLimitError,
84
7
  SyncsnapServer
85
8
  };
86
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type {\n CreateJobResponse,\n Job,\n PresignedUrlResponse,\n WaitForJobOptions,\n} from './types';\n\nconst SYNCSNAP_API_BASE_URL = 'https://api.syncsnap.xyz/api';\n\nexport class SyncsnapServer {\n private readonly baseUrl: string;\n private readonly token: string;\n private readonly fetcher: typeof fetch;\n\n constructor(apiVersion: string = 'v1') {\n this.baseUrl = `${SYNCSNAP_API_BASE_URL}/${apiVersion}`;\n this.token = process.env.SYNCSNAP_TOKEN!;\n this.fetcher = fetch;\n }\n\n private authHeaders(): HeadersInit {\n return {\n 'Content-Type': 'application/json',\n 'X-API-KEY': this.token,\n };\n }\n\n private async requestJson<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await this.fetcher(url, init);\n const data = (await res.json().catch(() => ({}))) as T & {\n error?: string;\n };\n\n if (!res.ok) {\n const message =\n typeof data === 'object' && data && 'error' in data && data.error\n ? String(data.error)\n : `Syncsnap request failed (${res.status})`;\n throw new Error(message);\n }\n\n return data as T;\n }\n\n async createJob(): Promise<CreateJobResponse> {\n const url = `${this.baseUrl}/jobs`;\n return this.requestJson<CreateJobResponse>(url, {\n method: 'POST',\n headers: this.authHeaders(),\n });\n }\n\n async getJob(jobId: string): Promise<Job> {\n const url = `${this.baseUrl}/jobs/${encodeURIComponent(jobId)}`;\n return this.requestJson<Job>(url, {\n method: 'GET',\n headers: this.authHeaders(),\n });\n }\n\n async getUploadUrl(\n jobId: string,\n options: { fileName: string; expirationMinutes?: number }\n ): Promise<PresignedUrlResponse> {\n const url = `${this.baseUrl}/jobs/${encodeURIComponent(\n jobId\n )}/presigned-upload-url`;\n return this.requestJson<PresignedUrlResponse>(url, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n file_name: options.fileName,\n expiration: options.expirationMinutes,\n }),\n });\n }\n\n async getDownloadUrl(\n jobId: string,\n options?: { expirationMinutes?: number }\n ): Promise<PresignedUrlResponse> {\n const url = `${this.baseUrl}/jobs/${encodeURIComponent(\n jobId\n )}/presigned-download-url`;\n return this.requestJson<PresignedUrlResponse>(url, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n expiration: options?.expirationMinutes,\n }),\n });\n }\n\n async waitForJobCompletion(\n jobId: string,\n options: WaitForJobOptions = {}\n ): Promise<Job> {\n const intervalMs = options.intervalMs ?? 2000;\n const timeoutMs = options.timeoutMs ?? 120000;\n const startedAt = Date.now();\n\n while (true) {\n if (options.signal?.aborted) {\n throw new Error('Polling aborted');\n }\n if (Date.now() - startedAt > timeoutMs) {\n throw new Error('Polling timed out');\n }\n\n const job = await this.getJob(jobId);\n options.onPoll?.(job);\n\n if (job.status === 'completed' || job.status === 'failed') {\n return job;\n }\n\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n }\n}\n"],"mappings":";AAOA,IAAM,wBAAwB;AAEvB,IAAM,iBAAN,MAAqB;AAAA,EAK1B,YAAY,aAAqB,MAAM;AACrC,SAAK,UAAU,GAAG,qBAAqB,IAAI,UAAU;AACrD,SAAK,QAAQ,QAAQ,IAAI;AACzB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,cAA2B;AACjC,WAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,aAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,MAAc,YAAe,KAAa,MAAgC;AACxE,UAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,IAAI;AACxC,UAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAI/C,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UACJ,OAAO,SAAS,YAAY,QAAQ,WAAW,QAAQ,KAAK,QACxD,OAAO,KAAK,KAAK,IACjB,4BAA4B,IAAI,MAAM;AAC5C,YAAM,IAAI,MAAM,OAAO;AAAA,IACzB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAwC;AAC5C,UAAM,MAAM,GAAG,KAAK,OAAO;AAC3B,WAAO,KAAK,YAA+B,KAAK;AAAA,MAC9C,QAAQ;AAAA,MACR,SAAS,KAAK,YAAY;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,OAA6B;AACxC,UAAM,MAAM,GAAG,KAAK,OAAO,SAAS,mBAAmB,KAAK,CAAC;AAC7D,WAAO,KAAK,YAAiB,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS,KAAK,YAAY;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aACJ,OACA,SAC+B;AAC/B,UAAM,MAAM,GAAG,KAAK,OAAO,SAAS;AAAA,MAClC;AAAA,IACF,CAAC;AACD,WAAO,KAAK,YAAkC,KAAK;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS,KAAK,YAAY;AAAA,MAC1B,MAAM,KAAK,UAAU;AAAA,QACnB,WAAW,QAAQ;AAAA,QACnB,YAAY,QAAQ;AAAA,MACtB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,eACJ,OACA,SAC+B;AAC/B,UAAM,MAAM,GAAG,KAAK,OAAO,SAAS;AAAA,MAClC;AAAA,IACF,CAAC;AACD,WAAO,KAAK,YAAkC,KAAK;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS,KAAK,YAAY;AAAA,MAC1B,MAAM,KAAK,UAAU;AAAA,QACnB,YAAY,SAAS;AAAA,MACvB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,qBACJ,OACA,UAA6B,CAAC,GAChB;AACd,UAAM,aAAa,QAAQ,cAAc;AACzC,UAAM,YAAY,QAAQ,aAAa;AACvC,UAAM,YAAY,KAAK,IAAI;AAE3B,WAAO,MAAM;AACX,UAAI,QAAQ,QAAQ,SAAS;AAC3B,cAAM,IAAI,MAAM,iBAAiB;AAAA,MACnC;AACA,UAAI,KAAK,IAAI,IAAI,YAAY,WAAW;AACtC,cAAM,IAAI,MAAM,mBAAmB;AAAA,MACrC;AAEA,YAAM,MAAM,MAAM,KAAK,OAAO,KAAK;AACnC,cAAQ,SAAS,GAAG;AAEpB,UAAI,IAAI,WAAW,eAAe,IAAI,WAAW,UAAU;AACzD,eAAO;AAAA,MACT;AAEA,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAAA,IAChE;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/next.js CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ SyncsnapRateLimitError
3
+ } from "./chunk-SUNHYVAG.js";
4
+
1
5
  // src/next/route-handlers.ts
2
6
  function json(data, init) {
3
7
  return new Response(JSON.stringify(data), {
@@ -45,8 +49,15 @@ function getCatchAllPath(params) {
45
49
  }
46
50
  function createJobHandler(client) {
47
51
  return async function POST() {
48
- const job = await client.createJob();
49
- return json(job);
52
+ try {
53
+ const job = await client.createJob();
54
+ return json(job);
55
+ } catch (err) {
56
+ if (err instanceof SyncsnapRateLimitError) {
57
+ return json({ error: err.message }, { status: 429 });
58
+ }
59
+ throw err;
60
+ }
50
61
  };
51
62
  }
52
63
  function getJobHandler(client) {
@@ -55,8 +66,15 @@ function getJobHandler(client) {
55
66
  if (!resolved?.id) {
56
67
  return json({ error: "Job id is required" }, { status: 400 });
57
68
  }
58
- const job = await client.getJob(resolved.id);
59
- return json(job);
69
+ try {
70
+ const job = await client.getJob(resolved.id);
71
+ return json(job);
72
+ } catch (err) {
73
+ if (err instanceof SyncsnapRateLimitError) {
74
+ return json({ error: err.message }, { status: 429 });
75
+ }
76
+ throw err;
77
+ }
60
78
  };
61
79
  }
62
80
  function getDownloadUrlHandler(client) {
@@ -65,12 +83,19 @@ function getDownloadUrlHandler(client) {
65
83
  if (!resolved?.id) {
66
84
  return json({ error: "Job id is required" }, { status: 400 });
67
85
  }
68
- const expirationMinutes = parseExpiration(request.url);
69
- const response = await client.getDownloadUrl(
70
- resolved.id,
71
- { expirationMinutes }
72
- );
73
- return json(response);
86
+ try {
87
+ const expirationMinutes = parseExpiration(request.url);
88
+ const response = await client.getDownloadUrl(
89
+ resolved.id,
90
+ { expirationMinutes }
91
+ );
92
+ return json(response);
93
+ } catch (err) {
94
+ if (err instanceof SyncsnapRateLimitError) {
95
+ return json({ error: err.message }, { status: 429 });
96
+ }
97
+ throw err;
98
+ }
74
99
  };
75
100
  }
76
101
  function createRouteHandler(options) {
@@ -80,30 +105,44 @@ function createRouteHandler(options) {
80
105
  const resolved = await resolveParams(context.params);
81
106
  const segments = getCatchAllPath(resolved);
82
107
  if (segments.length === 2 && segments[0] === "job") {
83
- const job = await client.getJob(segments[1]);
84
- return json(job);
108
+ try {
109
+ const job = await client.getJob(segments[1]);
110
+ return json(job);
111
+ } catch (err) {
112
+ if (err instanceof SyncsnapRateLimitError) {
113
+ return json({ error: err.message }, { status: 429 });
114
+ }
115
+ throw err;
116
+ }
85
117
  }
86
118
  if (segments.length === 3 && segments[0] === "job" && segments[2] === "download") {
87
119
  const jobId = segments[1];
88
- const job = await client.getJob(jobId);
89
- if (job.status !== "completed") {
90
- return json({ error: "Job is not completed" }, { status: 400 });
91
- }
92
- const expirationMinutes = parseExpiration(request.url);
93
- const presigned = await client.getDownloadUrl(
94
- jobId,
95
- {
96
- expirationMinutes
120
+ try {
121
+ const job = await client.getJob(jobId);
122
+ if (job.status !== "completed") {
123
+ return json({ error: "Job is not completed" }, { status: 400 });
97
124
  }
98
- );
99
- const response = { ...presigned };
100
- if (onCompletedCallback) {
101
- const payload = await onCompletedCallback(job, presigned);
102
- if (payload !== void 0) {
103
- response.completedPayload = payload;
125
+ const expirationMinutes = parseExpiration(request.url);
126
+ const presigned = await client.getDownloadUrl(
127
+ jobId,
128
+ {
129
+ expirationMinutes
130
+ }
131
+ );
132
+ const response = { ...presigned };
133
+ if (onCompletedCallback) {
134
+ const payload = await onCompletedCallback(job, presigned);
135
+ if (payload !== void 0) {
136
+ response.completedPayload = payload;
137
+ }
138
+ }
139
+ return json(response);
140
+ } catch (err) {
141
+ if (err instanceof SyncsnapRateLimitError) {
142
+ return json({ error: err.message }, { status: 429 });
104
143
  }
144
+ throw err;
105
145
  }
106
- return json(response);
107
146
  }
108
147
  if (segments.length === 3 && segments[0] === "job" && segments[2] === "wait") {
109
148
  const jobId = segments[1];
@@ -128,6 +167,9 @@ function createRouteHandler(options) {
128
167
  }
129
168
  return json(body);
130
169
  } catch (err) {
170
+ if (err instanceof SyncsnapRateLimitError) {
171
+ return json({ error: err.message }, { status: 429 });
172
+ }
131
173
  const message = err instanceof Error ? err.message : "Wait failed";
132
174
  const isTimeout = message.includes("timed out") || message.includes("Polling timed out");
133
175
  return json({ error: message }, { status: isTimeout ? 408 : 500 });
@@ -139,8 +181,15 @@ function createRouteHandler(options) {
139
181
  const resolved = await resolveParams(context.params);
140
182
  const segments = getCatchAllPath(resolved);
141
183
  if (segments.length === 1 && segments[0] === "job") {
142
- const job = await client.createJob();
143
- return json(job);
184
+ try {
185
+ const job = await client.createJob();
186
+ return json(job);
187
+ } catch (err) {
188
+ if (err instanceof SyncsnapRateLimitError) {
189
+ return json({ error: err.message }, { status: 429 });
190
+ }
191
+ throw err;
192
+ }
144
193
  }
145
194
  return json({ error: "Not found" }, { status: 404 });
146
195
  }
package/dist/next.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/next/route-handlers.ts"],"sourcesContent":["import type { SyncsnapServer } from '../client';\nimport type {\n DownloadUrlResponse,\n Job,\n PresignedUrlResponse,\n WaitCompletionResponse,\n} from '../types';\n\ntype NextParams = { params: { id: string } | Promise<{ id: string }> };\ntype CatchAllParams = {\n params?:\n | Record<string, string | string[] | undefined>\n | Promise<Record<string, string | string[] | undefined>>;\n};\n\nfunction json(data: unknown, init?: ResponseInit): Response {\n return new Response(JSON.stringify(data), {\n ...init,\n headers: {\n 'Content-Type': 'application/json',\n ...(init?.headers ?? {}),\n },\n });\n}\n\nfunction parseExpiration(url: string): number | undefined {\n const { searchParams } = new URL(url);\n const raw = searchParams.get('expiration');\n if (!raw) return undefined;\n const value = Number(raw);\n return Number.isFinite(value) && value > 0 ? value : undefined;\n}\n\nfunction parseWaitOptions(url: string): {\n timeoutMs?: number;\n intervalMs?: number;\n} {\n const { searchParams } = new URL(url);\n const timeoutMs = parseOptionalPositiveNumber(searchParams.get('timeoutMs'));\n const intervalMs = parseOptionalPositiveNumber(\n searchParams.get('intervalMs')\n );\n return { timeoutMs, intervalMs };\n}\n\nfunction parseOptionalPositiveNumber(value: string | null): number | undefined {\n if (value == null) return undefined;\n const n = Number(value);\n return Number.isFinite(n) && n > 0 ? n : undefined;\n}\n\nasync function resolveParams<T>(\n params?: T | Promise<T>\n): Promise<T | undefined> {\n if (!params) return undefined;\n if (typeof (params as Promise<T>).then === 'function') {\n return await (params as Promise<T>);\n }\n return params as T;\n}\n\nfunction getCatchAllPath(\n params?: Record<string, string | string[] | undefined>\n): string[] {\n if (!params) return [];\n for (const value of Object.values(params)) {\n if (Array.isArray(value)) return value;\n if (typeof value === 'string' && value.length > 0) return [value];\n }\n return [];\n}\n\nexport function createJobHandler(client: SyncsnapServer) {\n return async function POST(): Promise<Response> {\n const job = await client.createJob();\n return json(job);\n };\n}\n\nexport function getJobHandler(client: SyncsnapServer) {\n return async function GET(\n _request: Request,\n context: NextParams\n ): Promise<Response> {\n const resolved = await resolveParams(context.params);\n if (!resolved?.id) {\n return json({ error: 'Job id is required' }, { status: 400 });\n }\n const job = await client.getJob(resolved.id);\n return json(job);\n };\n}\n\nexport function getDownloadUrlHandler(client: SyncsnapServer) {\n return async function GET(\n request: Request,\n context: NextParams\n ): Promise<Response> {\n const resolved = await resolveParams(context.params);\n if (!resolved?.id) {\n return json({ error: 'Job id is required' }, { status: 400 });\n }\n const expirationMinutes = parseExpiration(request.url);\n const response: PresignedUrlResponse = await client.getDownloadUrl(\n resolved.id,\n { expirationMinutes }\n );\n return json(response);\n };\n}\n\nexport interface CreateRouteHandlerOptions {\n client: SyncsnapServer;\n /**\n * Called when a job completes (after server-side polling). Receives the job and, when\n * status is \"completed\", the presigned download URL. Whatever this returns is sent to\n * the client and passed to `useSyncsnapJob`'s `onCompleted(job, result)`.\n */\n onCompleted?: (\n job: Job,\n presigned?: PresignedUrlResponse\n ) => unknown | Promise<unknown>;\n}\n\nexport function createRouteHandler(options: CreateRouteHandlerOptions) {\n const { client, onCompleted: onCompletedCallback } = options;\n\n return {\n GET: async (\n request: Request,\n context: CatchAllParams\n ): Promise<Response> => {\n const resolved = await resolveParams(context.params);\n const segments = getCatchAllPath(resolved);\n if (segments.length === 2 && segments[0] === 'job') {\n const job = await client.getJob(segments[1]);\n return json(job);\n }\n\n if (\n segments.length === 3 &&\n segments[0] === 'job' &&\n segments[2] === 'download'\n ) {\n const jobId = segments[1];\n const job = await client.getJob(jobId);\n if (job.status !== 'completed') {\n return json({ error: 'Job is not completed' }, { status: 400 });\n }\n const expirationMinutes = parseExpiration(request.url);\n const presigned: PresignedUrlResponse = await client.getDownloadUrl(\n jobId,\n {\n expirationMinutes,\n }\n );\n const response: DownloadUrlResponse = { ...presigned };\n if (onCompletedCallback) {\n const payload = await onCompletedCallback(job, presigned);\n if (payload !== undefined) {\n response.completedPayload = payload;\n }\n }\n return json(response);\n }\n\n if (\n segments.length === 3 &&\n segments[0] === 'job' &&\n segments[2] === 'wait'\n ) {\n const jobId = segments[1];\n const { timeoutMs, intervalMs } = parseWaitOptions(request.url);\n try {\n const job = await client.waitForJobCompletion(jobId, {\n timeoutMs,\n intervalMs,\n });\n const body: WaitCompletionResponse = { job };\n if (job.status === 'completed') {\n const presigned: PresignedUrlResponse =\n await client.getDownloadUrl(jobId);\n const result = onCompletedCallback\n ? await onCompletedCallback(job, presigned)\n : presigned;\n if (result !== undefined) {\n body.result = result;\n }\n } else if (onCompletedCallback) {\n const result = await onCompletedCallback(job);\n if (result !== undefined) {\n body.result = result;\n }\n }\n return json(body);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Wait failed';\n const isTimeout =\n message.includes('timed out') ||\n message.includes('Polling timed out');\n return json({ error: message }, { status: isTimeout ? 408 : 500 });\n }\n }\n\n return json({ error: 'Not found' }, { status: 404 });\n },\n POST: async (\n _request: Request,\n context: CatchAllParams\n ): Promise<Response> => {\n const resolved = await resolveParams(context.params);\n const segments = getCatchAllPath(resolved);\n if (segments.length === 1 && segments[0] === 'job') {\n const job = await client.createJob();\n return json(job);\n }\n\n return json({ error: 'Not found' }, { status: 404 });\n },\n };\n}\n\nexport type { Job };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/next/route-handlers.ts"],"sourcesContent":["import type { SyncsnapServer } from '../client';\nimport { SyncsnapRateLimitError } from '../client';\nimport type {\n DownloadUrlResponse,\n Job,\n PresignedUrlResponse,\n WaitCompletionResponse,\n} from '../types';\n\ntype NextParams = { params: { id: string } | Promise<{ id: string }> };\ntype CatchAllParams = {\n params?:\n | Record<string, string | string[] | undefined>\n | Promise<Record<string, string | string[] | undefined>>;\n};\n\nfunction json(data: unknown, init?: ResponseInit): Response {\n return new Response(JSON.stringify(data), {\n ...init,\n headers: {\n 'Content-Type': 'application/json',\n ...(init?.headers ?? {}),\n },\n });\n}\n\nfunction parseExpiration(url: string): number | undefined {\n const { searchParams } = new URL(url);\n const raw = searchParams.get('expiration');\n if (!raw) return undefined;\n const value = Number(raw);\n return Number.isFinite(value) && value > 0 ? value : undefined;\n}\n\nfunction parseWaitOptions(url: string): {\n timeoutMs?: number;\n intervalMs?: number;\n} {\n const { searchParams } = new URL(url);\n const timeoutMs = parseOptionalPositiveNumber(searchParams.get('timeoutMs'));\n const intervalMs = parseOptionalPositiveNumber(\n searchParams.get('intervalMs')\n );\n return { timeoutMs, intervalMs };\n}\n\nfunction parseOptionalPositiveNumber(value: string | null): number | undefined {\n if (value == null) return undefined;\n const n = Number(value);\n return Number.isFinite(n) && n > 0 ? n : undefined;\n}\n\nasync function resolveParams<T>(\n params?: T | Promise<T>\n): Promise<T | undefined> {\n if (!params) return undefined;\n if (typeof (params as Promise<T>).then === 'function') {\n return await (params as Promise<T>);\n }\n return params as T;\n}\n\nfunction getCatchAllPath(\n params?: Record<string, string | string[] | undefined>\n): string[] {\n if (!params) return [];\n for (const value of Object.values(params)) {\n if (Array.isArray(value)) return value;\n if (typeof value === 'string' && value.length > 0) return [value];\n }\n return [];\n}\n\nexport function createJobHandler(client: SyncsnapServer) {\n return async function POST(): Promise<Response> {\n try {\n const job = await client.createJob();\n return json(job);\n } catch (err) {\n if (err instanceof SyncsnapRateLimitError) {\n return json({ error: err.message }, { status: 429 });\n }\n throw err;\n }\n };\n}\n\nexport function getJobHandler(client: SyncsnapServer) {\n return async function GET(\n _request: Request,\n context: NextParams\n ): Promise<Response> {\n const resolved = await resolveParams(context.params);\n if (!resolved?.id) {\n return json({ error: 'Job id is required' }, { status: 400 });\n }\n try {\n const job = await client.getJob(resolved.id);\n return json(job);\n } catch (err) {\n if (err instanceof SyncsnapRateLimitError) {\n return json({ error: err.message }, { status: 429 });\n }\n throw err;\n }\n };\n}\n\nexport function getDownloadUrlHandler(client: SyncsnapServer) {\n return async function GET(\n request: Request,\n context: NextParams\n ): Promise<Response> {\n const resolved = await resolveParams(context.params);\n if (!resolved?.id) {\n return json({ error: 'Job id is required' }, { status: 400 });\n }\n try {\n const expirationMinutes = parseExpiration(request.url);\n const response: PresignedUrlResponse = await client.getDownloadUrl(\n resolved.id,\n { expirationMinutes }\n );\n return json(response);\n } catch (err) {\n if (err instanceof SyncsnapRateLimitError) {\n return json({ error: err.message }, { status: 429 });\n }\n throw err;\n }\n };\n}\n\nexport interface CreateRouteHandlerOptions {\n client: SyncsnapServer;\n /**\n * Called when a job completes (after server-side polling). Receives the job and, when\n * status is \"completed\", the presigned download URL. Whatever this returns is sent to\n * the client and passed to `useSyncsnapJob`'s `onCompleted(job, result)`.\n */\n onCompleted?: (\n job: Job,\n presigned?: PresignedUrlResponse\n ) => unknown | Promise<unknown>;\n}\n\nexport function createRouteHandler(options: CreateRouteHandlerOptions) {\n const { client, onCompleted: onCompletedCallback } = options;\n\n return {\n GET: async (\n request: Request,\n context: CatchAllParams\n ): Promise<Response> => {\n const resolved = await resolveParams(context.params);\n const segments = getCatchAllPath(resolved);\n if (segments.length === 2 && segments[0] === 'job') {\n try {\n const job = await client.getJob(segments[1]);\n return json(job);\n } catch (err) {\n if (err instanceof SyncsnapRateLimitError) {\n return json({ error: err.message }, { status: 429 });\n }\n throw err;\n }\n }\n\n if (\n segments.length === 3 &&\n segments[0] === 'job' &&\n segments[2] === 'download'\n ) {\n const jobId = segments[1];\n try {\n const job = await client.getJob(jobId);\n if (job.status !== 'completed') {\n return json({ error: 'Job is not completed' }, { status: 400 });\n }\n const expirationMinutes = parseExpiration(request.url);\n const presigned: PresignedUrlResponse = await client.getDownloadUrl(\n jobId,\n {\n expirationMinutes,\n }\n );\n const response: DownloadUrlResponse = { ...presigned };\n if (onCompletedCallback) {\n const payload = await onCompletedCallback(job, presigned);\n if (payload !== undefined) {\n response.completedPayload = payload;\n }\n }\n return json(response);\n } catch (err) {\n if (err instanceof SyncsnapRateLimitError) {\n return json({ error: err.message }, { status: 429 });\n }\n throw err;\n }\n }\n\n if (\n segments.length === 3 &&\n segments[0] === 'job' &&\n segments[2] === 'wait'\n ) {\n const jobId = segments[1];\n const { timeoutMs, intervalMs } = parseWaitOptions(request.url);\n try {\n const job = await client.waitForJobCompletion(jobId, {\n timeoutMs,\n intervalMs,\n });\n const body: WaitCompletionResponse = { job };\n if (job.status === 'completed') {\n const presigned: PresignedUrlResponse =\n await client.getDownloadUrl(jobId);\n const result = onCompletedCallback\n ? await onCompletedCallback(job, presigned)\n : presigned;\n if (result !== undefined) {\n body.result = result;\n }\n } else if (onCompletedCallback) {\n const result = await onCompletedCallback(job);\n if (result !== undefined) {\n body.result = result;\n }\n }\n return json(body);\n } catch (err) {\n if (err instanceof SyncsnapRateLimitError) {\n return json({ error: err.message }, { status: 429 });\n }\n const message = err instanceof Error ? err.message : 'Wait failed';\n const isTimeout =\n message.includes('timed out') ||\n message.includes('Polling timed out');\n return json({ error: message }, { status: isTimeout ? 408 : 500 });\n }\n }\n\n return json({ error: 'Not found' }, { status: 404 });\n },\n POST: async (\n _request: Request,\n context: CatchAllParams\n ): Promise<Response> => {\n const resolved = await resolveParams(context.params);\n const segments = getCatchAllPath(resolved);\n if (segments.length === 1 && segments[0] === 'job') {\n try {\n const job = await client.createJob();\n return json(job);\n } catch (err) {\n if (err instanceof SyncsnapRateLimitError) {\n return json({ error: err.message }, { status: 429 });\n }\n throw err;\n }\n }\n\n return json({ error: 'Not found' }, { status: 404 });\n },\n };\n}\n\nexport type { Job };\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "syncsnap",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Syncsnap server SDK for Node/Next.js",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,12 +21,12 @@
21
21
  ],
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "https://github.com/syncsnap/packages.git",
24
+ "url": "https://github.com/syncsnapxyz/packages.git",
25
25
  "directory": "syncsnap"
26
26
  },
27
- "homepage": "https://github.com/syncsnap/packages/syncsnap#readme",
27
+ "homepage": "https://github.com/syncsnapxyz/packages/syncsnap#readme",
28
28
  "bugs": {
29
- "url": "https://github.com/syncsnap/packages/syncsnap/issues"
29
+ "url": "https://github.com/syncsnapxyz/packages/syncsnap/issues"
30
30
  },
31
31
  "keywords": [
32
32
  "syncsnap",
@@ -49,7 +49,8 @@
49
49
  "format": "prettier --write .",
50
50
  "format:check": "prettier --check .",
51
51
  "build": "tsup src/index.ts src/next.ts --dts --format esm --out-dir dist --clean --sourcemap",
52
- "publish": "npm publish --provenance --access public",
52
+ "publish": "npm publish --access public",
53
+ "publish:ci": "npm publish --provenance --access public",
53
54
  "link": "npm run build && npm link",
54
55
  "prepublishOnly": "npm run build",
55
56
  "test": "vitest run",