@hot-updater/supabase 1.0.0-rc.21 → 1.0.0-rc.22
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/dist/edge.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_plugins = require("./plugins-
|
|
2
|
+
const require_plugins = require("./plugins-jpIkI_T2.cjs");
|
|
3
3
|
//#region src/supabaseEdgeFunctionStorage.ts
|
|
4
4
|
const supabaseEdgeFunctionStorage = (config) => require_plugins.supabaseStorage(config);
|
|
5
5
|
//#endregion
|
package/dist/edge.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as supabaseStorage, r as supabaseDatabase, t as plugins } from "./plugins-
|
|
1
|
+
import { n as supabaseStorage, r as supabaseDatabase, t as plugins } from "./plugins-Dj-4Pk3j.mjs";
|
|
2
2
|
//#region src/supabaseEdgeFunctionStorage.ts
|
|
3
3
|
const supabaseEdgeFunctionStorage = (config) => supabaseStorage(config);
|
|
4
4
|
//#endregion
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_plugins = require("./plugins-
|
|
2
|
+
const require_plugins = require("./plugins-jpIkI_T2.cjs");
|
|
3
3
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
4
4
|
//#region src/supabaseSchema.ts
|
|
5
5
|
/** Every table the apply RPC may name: models, index tables, settings, and the write guard. */
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as SUPABASE_TABLE_PREFIX, i as SUPABASE_APPLY_FUNCTION, n as supabaseStorage, r as supabaseDatabase$1, t as plugins } from "./plugins-
|
|
1
|
+
import { a as SUPABASE_TABLE_PREFIX, i as SUPABASE_APPLY_FUNCTION, n as supabaseStorage, r as supabaseDatabase$1, t as plugins } from "./plugins-Dj-4Pk3j.mjs";
|
|
2
2
|
import { SETTINGS_TABLE, WRITE_GUARD_TABLE, coreSettings, coreTarget, createTableStatements, generateEngineSql, isMultiIndex } from "@hot-updater/plugin-core";
|
|
3
3
|
//#region src/supabaseSchema.ts
|
|
4
4
|
/** Every table the apply RPC may name: models, index tables, settings, and the write guard. */
|
|
@@ -129,13 +129,44 @@ const createSupabaseSignedUrlBatcher = ({ createSignedUrls, expiresIn, formatObj
|
|
|
129
129
|
//#endregion
|
|
130
130
|
//#region src/supabaseStorage.ts
|
|
131
131
|
const isNotFoundError = (error) => error?.message?.toLowerCase().includes("not found") === true;
|
|
132
|
+
/**
|
|
133
|
+
* Storage's unread response to a download or HEAD request it failed, which
|
|
134
|
+
* supabase-js wraps in a StorageUnknownError.
|
|
135
|
+
*/
|
|
136
|
+
const failedResponseOf = (error) => {
|
|
137
|
+
if (typeof error !== "object" || error === null || Reflect.get(error, "name") !== "StorageUnknownError") return;
|
|
138
|
+
const response = Reflect.get(error, "originalError");
|
|
139
|
+
if (typeof response !== "object" || response === null || typeof Reflect.get(response, "status") !== "number") return;
|
|
140
|
+
return response;
|
|
141
|
+
};
|
|
142
|
+
/** Storage answers a HEAD request for a missing object with 400 or 404. */
|
|
132
143
|
const isMissingExistsError = (error) => {
|
|
133
|
-
|
|
134
|
-
const originalError = Reflect.get(error, "originalError");
|
|
135
|
-
if (typeof originalError !== "object" || originalError === null) return false;
|
|
136
|
-
const status = Reflect.get(originalError, "status");
|
|
144
|
+
const status = failedResponseOf(error)?.status;
|
|
137
145
|
return status === 400 || status === 404;
|
|
138
146
|
};
|
|
147
|
+
/**
|
|
148
|
+
* Storage answers a download of a missing object with 400 or 404 and
|
|
149
|
+
* `error: "not_found"`, and a missing bucket with another error.
|
|
150
|
+
*/
|
|
151
|
+
const isMissingObjectError = async (error) => {
|
|
152
|
+
const response = failedResponseOf(error);
|
|
153
|
+
if (response?.status !== 400 && response?.status !== 404) return false;
|
|
154
|
+
const body = await response.json().catch(() => void 0);
|
|
155
|
+
return typeof body === "object" && body !== null && Reflect.get(body, "error") === "not_found";
|
|
156
|
+
};
|
|
157
|
+
/** A character Storage rejects in an object name, `?`, or `!`. */
|
|
158
|
+
const ESCAPED_CHARACTER = /[^A-Za-z0-9_/.*'() &$=@;:+,-]/gu;
|
|
159
|
+
const utf8 = new TextEncoder();
|
|
160
|
+
/**
|
|
161
|
+
* The object name Storage keeps a key under. Storage accepts names of ASCII
|
|
162
|
+
* letters, digits and `_/!.*'() &$=@;:+,?-` only, and supabase-js puts a name
|
|
163
|
+
* in the request URL unencoded, where `?` ends the path. So every other
|
|
164
|
+
* character of the key, `?`, and the escape character `!` are stored as `!`
|
|
165
|
+
* and the two hex digits of each of their UTF-8 bytes, such as `#` as `!23`.
|
|
166
|
+
* Distinct keys keep distinct names, and the names need no URL encoding.
|
|
167
|
+
* Deploy's keys, such as `<bundleId>/manifest.json`, are stored as they are.
|
|
168
|
+
*/
|
|
169
|
+
const toObjectName = (key) => key.replace(ESCAPED_CHARACTER, (character) => Array.from(utf8.encode(character), (byte) => `!${byte.toString(16).toUpperCase().padStart(2, "0")}`).join(""));
|
|
139
170
|
const supabaseStorage = (config) => {
|
|
140
171
|
const bucket = createClient(config.supabaseUrl, resolveSupabaseServiceRoleKey(config)).storage.from(config.bucketName);
|
|
141
172
|
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
@@ -154,7 +185,7 @@ const supabaseStorage = (config) => {
|
|
|
154
185
|
protocol: "supabase-storage",
|
|
155
186
|
async put({ key, body, contentLength, contentType }) {
|
|
156
187
|
const storageKey = getStorageKey(key);
|
|
157
|
-
const { error } = await bucket.upload(storageKey, body, {
|
|
188
|
+
const { error } = await bucket.upload(toObjectName(storageKey), body, {
|
|
158
189
|
contentType,
|
|
159
190
|
cacheControl: "max-age=31536000",
|
|
160
191
|
upsert: true,
|
|
@@ -170,20 +201,20 @@ const supabaseStorage = (config) => {
|
|
|
170
201
|
},
|
|
171
202
|
async get({ storageUri }) {
|
|
172
203
|
const { key } = parseAndValidate(storageUri);
|
|
173
|
-
const { data, error } = await bucket.download(key);
|
|
204
|
+
const { data, error } = await bucket.download(toObjectName(key));
|
|
174
205
|
if (error) {
|
|
175
|
-
if (
|
|
206
|
+
if (await isMissingObjectError(error)) return { response: null };
|
|
176
207
|
throw new Error(`Failed to download storage object: ${error.message}`);
|
|
177
208
|
}
|
|
178
209
|
return { response: data ? new Response(data) : null };
|
|
179
210
|
},
|
|
180
211
|
async getDownloadUrl({ storageUri }) {
|
|
181
212
|
const { key } = parseAndValidate(storageUri);
|
|
182
|
-
return { url: await resolveSignedUrl(config.bucketName, key) };
|
|
213
|
+
return { url: await resolveSignedUrl(config.bucketName, toObjectName(key)) };
|
|
183
214
|
},
|
|
184
215
|
async exists({ storageUri }) {
|
|
185
216
|
const { key } = parseAndValidate(storageUri);
|
|
186
|
-
const { data, error } = await bucket.exists(key);
|
|
217
|
+
const { data, error } = await bucket.exists(toObjectName(key));
|
|
187
218
|
if (error) {
|
|
188
219
|
if (isMissingExistsError(error)) return { exists: false };
|
|
189
220
|
throw error;
|
|
@@ -192,7 +223,7 @@ const supabaseStorage = (config) => {
|
|
|
192
223
|
},
|
|
193
224
|
async delete({ storageUri }) {
|
|
194
225
|
const { key } = parseAndValidate(storageUri);
|
|
195
|
-
const { error } = await bucket.remove([key]);
|
|
226
|
+
const { error } = await bucket.remove([toObjectName(key)]);
|
|
196
227
|
if (error && !isNotFoundError(error)) throw new Error(`Failed to delete storage object: ${error.message}`);
|
|
197
228
|
return { deleted: true };
|
|
198
229
|
}
|
|
@@ -129,13 +129,44 @@ const createSupabaseSignedUrlBatcher = ({ createSignedUrls, expiresIn, formatObj
|
|
|
129
129
|
//#endregion
|
|
130
130
|
//#region src/supabaseStorage.ts
|
|
131
131
|
const isNotFoundError = (error) => error?.message?.toLowerCase().includes("not found") === true;
|
|
132
|
+
/**
|
|
133
|
+
* Storage's unread response to a download or HEAD request it failed, which
|
|
134
|
+
* supabase-js wraps in a StorageUnknownError.
|
|
135
|
+
*/
|
|
136
|
+
const failedResponseOf = (error) => {
|
|
137
|
+
if (typeof error !== "object" || error === null || Reflect.get(error, "name") !== "StorageUnknownError") return;
|
|
138
|
+
const response = Reflect.get(error, "originalError");
|
|
139
|
+
if (typeof response !== "object" || response === null || typeof Reflect.get(response, "status") !== "number") return;
|
|
140
|
+
return response;
|
|
141
|
+
};
|
|
142
|
+
/** Storage answers a HEAD request for a missing object with 400 or 404. */
|
|
132
143
|
const isMissingExistsError = (error) => {
|
|
133
|
-
|
|
134
|
-
const originalError = Reflect.get(error, "originalError");
|
|
135
|
-
if (typeof originalError !== "object" || originalError === null) return false;
|
|
136
|
-
const status = Reflect.get(originalError, "status");
|
|
144
|
+
const status = failedResponseOf(error)?.status;
|
|
137
145
|
return status === 400 || status === 404;
|
|
138
146
|
};
|
|
147
|
+
/**
|
|
148
|
+
* Storage answers a download of a missing object with 400 or 404 and
|
|
149
|
+
* `error: "not_found"`, and a missing bucket with another error.
|
|
150
|
+
*/
|
|
151
|
+
const isMissingObjectError = async (error) => {
|
|
152
|
+
const response = failedResponseOf(error);
|
|
153
|
+
if (response?.status !== 400 && response?.status !== 404) return false;
|
|
154
|
+
const body = await response.json().catch(() => void 0);
|
|
155
|
+
return typeof body === "object" && body !== null && Reflect.get(body, "error") === "not_found";
|
|
156
|
+
};
|
|
157
|
+
/** A character Storage rejects in an object name, `?`, or `!`. */
|
|
158
|
+
const ESCAPED_CHARACTER = /[^A-Za-z0-9_/.*'() &$=@;:+,-]/gu;
|
|
159
|
+
const utf8 = new TextEncoder();
|
|
160
|
+
/**
|
|
161
|
+
* The object name Storage keeps a key under. Storage accepts names of ASCII
|
|
162
|
+
* letters, digits and `_/!.*'() &$=@;:+,?-` only, and supabase-js puts a name
|
|
163
|
+
* in the request URL unencoded, where `?` ends the path. So every other
|
|
164
|
+
* character of the key, `?`, and the escape character `!` are stored as `!`
|
|
165
|
+
* and the two hex digits of each of their UTF-8 bytes, such as `#` as `!23`.
|
|
166
|
+
* Distinct keys keep distinct names, and the names need no URL encoding.
|
|
167
|
+
* Deploy's keys, such as `<bundleId>/manifest.json`, are stored as they are.
|
|
168
|
+
*/
|
|
169
|
+
const toObjectName = (key) => key.replace(ESCAPED_CHARACTER, (character) => Array.from(utf8.encode(character), (byte) => `!${byte.toString(16).toUpperCase().padStart(2, "0")}`).join(""));
|
|
139
170
|
const supabaseStorage = (config) => {
|
|
140
171
|
const bucket = (0, _supabase_supabase_js.createClient)(config.supabaseUrl, resolveSupabaseServiceRoleKey(config)).storage.from(config.bucketName);
|
|
141
172
|
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
@@ -154,7 +185,7 @@ const supabaseStorage = (config) => {
|
|
|
154
185
|
protocol: "supabase-storage",
|
|
155
186
|
async put({ key, body, contentLength, contentType }) {
|
|
156
187
|
const storageKey = getStorageKey(key);
|
|
157
|
-
const { error } = await bucket.upload(storageKey, body, {
|
|
188
|
+
const { error } = await bucket.upload(toObjectName(storageKey), body, {
|
|
158
189
|
contentType,
|
|
159
190
|
cacheControl: "max-age=31536000",
|
|
160
191
|
upsert: true,
|
|
@@ -170,20 +201,20 @@ const supabaseStorage = (config) => {
|
|
|
170
201
|
},
|
|
171
202
|
async get({ storageUri }) {
|
|
172
203
|
const { key } = parseAndValidate(storageUri);
|
|
173
|
-
const { data, error } = await bucket.download(key);
|
|
204
|
+
const { data, error } = await bucket.download(toObjectName(key));
|
|
174
205
|
if (error) {
|
|
175
|
-
if (
|
|
206
|
+
if (await isMissingObjectError(error)) return { response: null };
|
|
176
207
|
throw new Error(`Failed to download storage object: ${error.message}`);
|
|
177
208
|
}
|
|
178
209
|
return { response: data ? new Response(data) : null };
|
|
179
210
|
},
|
|
180
211
|
async getDownloadUrl({ storageUri }) {
|
|
181
212
|
const { key } = parseAndValidate(storageUri);
|
|
182
|
-
return { url: await resolveSignedUrl(config.bucketName, key) };
|
|
213
|
+
return { url: await resolveSignedUrl(config.bucketName, toObjectName(key)) };
|
|
183
214
|
},
|
|
184
215
|
async exists({ storageUri }) {
|
|
185
216
|
const { key } = parseAndValidate(storageUri);
|
|
186
|
-
const { data, error } = await bucket.exists(key);
|
|
217
|
+
const { data, error } = await bucket.exists(toObjectName(key));
|
|
187
218
|
if (error) {
|
|
188
219
|
if (isMissingExistsError(error)) return { exists: false };
|
|
189
220
|
throw error;
|
|
@@ -192,7 +223,7 @@ const supabaseStorage = (config) => {
|
|
|
192
223
|
},
|
|
193
224
|
async delete({ storageUri }) {
|
|
194
225
|
const { key } = parseAndValidate(storageUri);
|
|
195
|
-
const { error } = await bucket.remove([key]);
|
|
226
|
+
const { error } = await bucket.remove([toObjectName(key)]);
|
|
196
227
|
if (error && !isNotFoundError(error)) throw new Error(`Failed to delete storage object: ${error.message}`);
|
|
197
228
|
return { deleted: true };
|
|
198
229
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/supabase",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.22",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"@supabase/supabase-js": "2.76.1",
|
|
52
52
|
"hono": "4.12.34",
|
|
53
53
|
"uuidv7": "^1.0.2",
|
|
54
|
-
"@hot-updater/protocol": "1.0.0-rc.
|
|
55
|
-
"@hot-updater/plugin-core": "1.0.0-rc.
|
|
56
|
-
"@hot-updater/cli-tools": "1.0.0-rc.
|
|
57
|
-
"@hot-updater/server": "1.0.0-rc.
|
|
54
|
+
"@hot-updater/protocol": "1.0.0-rc.22",
|
|
55
|
+
"@hot-updater/plugin-core": "1.0.0-rc.22",
|
|
56
|
+
"@hot-updater/cli-tools": "1.0.0-rc.22",
|
|
57
|
+
"@hot-updater/server": "1.0.0-rc.22"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@electric-sql/pglite": "0.4.1",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"execa": "9.5.2",
|
|
65
65
|
"@types/node": "^20",
|
|
66
66
|
"mime": "^4.0.4",
|
|
67
|
-
"@hot-updater/mock": "1.0.0-rc.
|
|
68
|
-
"@hot-updater/test-utils": "1.0.0-rc.
|
|
69
|
-
"@hot-updater/postgres": "1.0.0-rc.
|
|
67
|
+
"@hot-updater/mock": "1.0.0-rc.22",
|
|
68
|
+
"@hot-updater/test-utils": "1.0.0-rc.22",
|
|
69
|
+
"@hot-updater/postgres": "1.0.0-rc.22"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "tsdown",
|