appflare 0.2.26 → 0.2.28

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.
@@ -67,6 +67,26 @@ export function createStorageClient(
67
67
  ): StorageClient {
68
68
  return {
69
69
  upload: async (args) => {
70
+ let base64BodyToSend: string | undefined;
71
+
72
+ if (args.body instanceof File) {
73
+ const buffer = await args.body.arrayBuffer();
74
+ const bytes = new Uint8Array(buffer);
75
+ let binary = "";
76
+ const chunkSize = 8192;
77
+ for (let i = 0; i < bytes.length; i += chunkSize) {
78
+ const chunk = bytes.subarray(i, i + chunkSize);
79
+ binary += String.fromCharCode(...chunk);
80
+ }
81
+ base64BodyToSend = btoa(binary);
82
+ } else if (typeof args.body === "string") {
83
+ base64BodyToSend = btoa(args.body);
84
+ }
85
+
86
+ if (args.base64Body) {
87
+ base64BodyToSend = args.base64Body;
88
+ }
89
+
70
90
  const headers = await createAuthorizedHeaders(
71
91
  {
72
92
  "content-type": "application/json",
@@ -77,7 +97,12 @@ export function createStorageClient(
77
97
  const response = await request(\`\${endpoint}/storage/upload\`, {
78
98
  method: "POST",
79
99
  headers,
80
- body: JSON.stringify(args),
100
+ body: JSON.stringify({
101
+ path: args.path,
102
+ contentType: args.contentType,
103
+ expiresIn: args.expiresIn,
104
+ base64Body: base64BodyToSend,
105
+ }),
81
106
  });
82
107
  if (!response.ok) {
83
108
  throw new Error(await response.text());
@@ -105,24 +130,14 @@ export function createStorageClient(
105
130
 
106
131
  return (await response.json()) as StorageSignedUrlResponse;
107
132
  },
108
- preview: async (args) => {
133
+ preview: (args) => {
109
134
  const query = new URLSearchParams({
110
135
  path: args.path,
111
136
  ...(typeof args.expiresIn === "number"
112
137
  ? { expiresIn: String(args.expiresIn) }
113
138
  : {}),
114
139
  });
115
- const response = await request(
116
- \`\${endpoint}/storage/preview?\${query.toString()}\`,
117
- {
118
- headers: await createAuthorizedHeaders(undefined, onGetAuthToken),
119
- },
120
- );
121
- if (!response.ok) {
122
- throw new Error(await response.text());
123
- }
124
-
125
- return (await response.json()) as StorageSignedUrlResponse;
140
+ return \`\${endpoint}/storage/download?\${query.toString()}\`;
126
141
  },
127
142
  delete: async (args) => {
128
143
  const query = new URLSearchParams({
@@ -148,6 +148,8 @@ export type StorageClient = {
148
148
  path: string;
149
149
  contentType?: string;
150
150
  expiresIn?: number;
151
+ base64Body?: string;
152
+ body?: string | File;
151
153
  }) => Promise<StorageSignedUrlResponse>;
152
154
  download: (args: {
153
155
  path: string;
@@ -157,7 +159,7 @@ export type StorageClient = {
157
159
  preview: (args: {
158
160
  path: string;
159
161
  expiresIn?: number;
160
- }) => Promise<StorageSignedUrlResponse>;
162
+ }) => string;
161
163
  delete: (args: { path: string }) => Promise<{ ok: boolean; path: string }>;
162
164
  list: (args?: {
163
165
  prefix?: string;
@@ -32,28 +32,6 @@ function createStorageApi(
32
32
  contentType: args.contentType,
33
33
  });
34
34
 
35
- if (args.returnSignedUrlOnly) {
36
- const currentBucket = requireBucket();
37
- if (typeof currentBucket.createPresignedUrl !== "function") {
38
- throw new Error("R2 createPresignedUrl is unavailable for this runtime binding");
39
- }
40
-
41
- const signedRequest = buildSignedRequest(
42
- {
43
- path,
44
- method: "PUT",
45
- contentType: args.contentType,
46
- expiresIn: args.expiresIn,
47
- },
48
- path,
49
- );
50
- const signedUrl = await currentBucket.createPresignedUrl(signedRequest, {
51
- expiresIn: args.expiresIn ?? 60 * 5,
52
- });
53
-
54
- return signedUrl.toString();
55
- }
56
-
57
35
  return requireBucket().put(path, args.body, {
58
36
  httpMetadata: {
59
37
  ...(args.httpMetadata ?? {}),
@@ -98,36 +76,6 @@ function createStorageApi(
98
76
  include: args.include,
99
77
  });
100
78
  },
101
- signedUrl: async (args) => {
102
- const path = normalizeStoragePath(args.path);
103
- const requestMethod = args.method ?? "GET";
104
- const method: StorageMethod =
105
- requestMethod === "PUT"
106
- ? "put"
107
- : requestMethod === "DELETE"
108
- ? "delete"
109
- : args.downloadAsAttachment === false
110
- ? "preview"
111
- : "download";
112
-
113
- await assertAuthorized({
114
- path: "/" + path,
115
- method,
116
- contentType: args.contentType,
117
- });
118
-
119
- const currentBucket = requireBucket();
120
- if (typeof currentBucket.createPresignedUrl !== "function") {
121
- throw new Error("R2 createPresignedUrl is unavailable for this runtime binding");
122
- }
123
-
124
- const signedRequest = buildSignedRequest(args, path);
125
- const signedUrl = await currentBucket.createPresignedUrl(signedRequest, {
126
- expiresIn: args.expiresIn ?? 60 * 5,
127
- });
128
-
129
- return signedUrl.toString();
130
- },
131
79
  };
132
80
  }
133
81
  `;
@@ -37,25 +37,34 @@ export function registerGeneratedStorageRoutes(
37
37
  const path = toStoragePath(String(body.path ?? ""));
38
38
  const contentType =
39
39
  typeof body.contentType === "string" ? body.contentType : undefined;
40
- const expiresIn =
41
- typeof body.expiresIn === "number" && body.expiresIn > 0
42
- ? Math.floor(body.expiresIn)
43
- : undefined;
40
+ const encodedBody = typeof body.body === "string" ? body.body : undefined;
41
+ const base64Body = typeof body.base64Body === "string" ? body.base64Body : undefined;
42
+
43
+ if (!base64Body && !encodedBody) {
44
+ return c.json(
45
+ { message: "File content is required. Provide either 'body' or 'base64Body' field." },
46
+ 400,
47
+ );
48
+ }
49
+
50
+ const uploadBody = base64Body
51
+ ? Uint8Array.from(atob(base64Body), (char) => char.charCodeAt(0))
52
+ : encodedBody
53
+ ? Uint8Array.from(atob(encodedBody), (char) => char.charCodeAt(0))
54
+ : new Uint8Array();
44
55
 
45
- const url = await ctx.storage.put({
56
+ await ctx.storage.put({
46
57
  path,
47
- body: "",
58
+ body: uploadBody,
48
59
  contentType,
49
- expiresIn,
50
- returnSignedUrlOnly: true,
51
60
  });
52
61
 
53
62
  return c.json({
54
- url,
63
+ url: null,
55
64
  method: "PUT",
56
65
  path,
57
66
  contentType,
58
- expiresIn: expiresIn ?? 300,
67
+ uploaded: true,
59
68
  }, 200);
60
69
  } catch (error) {
61
70
  if (error instanceof AppflareHandledError) {
@@ -74,28 +83,28 @@ export function registerGeneratedStorageRoutes(
74
83
  try {
75
84
  const path = readStoragePath(c);
76
85
  const fileName = c.req.query("fileName") ?? undefined;
77
- const expiresIn = parseExpiresIn(c.req.query("expiresIn"));
78
- const url = await ctx.storage.signedUrl({
79
- path,
80
- method: "GET",
81
- expiresIn,
82
- downloadAsAttachment: true,
83
- fileName,
84
- });
86
+
87
+ const file = await ctx.storage.get({ path });
88
+ if (!file) {
89
+ return c.json({ message: "File not found" }, 404);
90
+ }
85
91
 
86
- return c.json({
87
- url,
88
- method: "GET",
89
- path,
90
- disposition: "attachment",
91
- }, 200);
92
+ const headers: Record<string, string> = {};
93
+ if (file.contentType) {
94
+ headers["content-type"] = file.contentType;
95
+ }
96
+ if (fileName) {
97
+ headers["content-disposition"] = \`attachment; filename="\${fileName}"\`;
98
+ }
99
+
100
+ return c.body(file.body, 200, headers);
92
101
  } catch (error) {
93
102
  if (error instanceof AppflareHandledError) {
94
103
  return c.json(error.payload, error.status as any);
95
104
  }
96
105
 
97
106
  return c.json(
98
- { message: (error as Error).message ?? "Unable to create download URL" },
107
+ { message: (error as Error).message ?? "Unable to download file" },
99
108
  400,
100
109
  );
101
110
  }
@@ -106,19 +115,13 @@ export function registerGeneratedStorageRoutes(
106
115
  try {
107
116
  const path = readStoragePath(c);
108
117
  const expiresIn = parseExpiresIn(c.req.query("expiresIn"));
109
- const url = await ctx.storage.signedUrl({
110
- path,
111
- method: "GET",
112
- expiresIn,
113
- downloadAsAttachment: false,
114
- });
115
-
116
- return c.json({
117
- url,
118
- method: "GET",
119
- path,
120
- disposition: "inline",
121
- }, 200);
118
+
119
+ // For now, we'll return an error since signed URLs are not available
120
+ // In the future, we could implement direct file serving here
121
+ return c.json(
122
+ { message: "Preview via signed URL is not available in this runtime. Use direct file access instead." },
123
+ 501,
124
+ );
122
125
  } catch (error) {
123
126
  if (error instanceof AppflareHandledError) {
124
127
  return c.json(error.payload, error.status as any);
@@ -49,7 +49,6 @@ export type AppflareStorage = {
49
49
  get: (args: StorageGetArgs) => Promise<unknown | null>;
50
50
  delete: (args: StorageDeleteArgs) => Promise<void>;
51
51
  list: (args?: StorageListArgs) => Promise<unknown>;
52
- signedUrl: (args: StorageSignedUrlArgs) => Promise<string>;
53
52
  };
54
53
 
55
54
  export type AppflareContext = {