@hot-updater/supabase 1.1.4 → 1.1.6
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 +1 -1
- package/dist/edge.d.cts +1 -1
- package/dist/edge.d.mts +1 -1
- package/dist/edge.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{supabaseStorage-DAl3pyb7.cjs → supabaseStorage-BJTIWSwx.cjs} +82 -0
- package/dist/{supabaseStorage-C8xQ3nyM.mjs → supabaseStorage-CKYtf3yW.mjs} +82 -0
- package/dist/{supabaseStorage-BryXQ72m.d.cts → supabaseStorage-CkNoVO3F.d.cts} +1 -1
- package/dist/{supabaseStorage-BryXQ72m.d.mts → supabaseStorage-CkNoVO3F.d.mts} +1 -1
- package/package.json +8 -8
package/dist/edge.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_supabaseStorage = require("./supabaseStorage-
|
|
2
|
+
const require_supabaseStorage = require("./supabaseStorage-BJTIWSwx.cjs");
|
|
3
3
|
exports.supabaseDatabase = require_supabaseStorage.supabaseDatabase;
|
|
4
4
|
exports.supabaseStorage = require_supabaseStorage.supabaseStorage;
|
package/dist/edge.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as supabaseDatabase, n as supabaseStorage, r as SupabaseDatabaseConfig, t as SupabaseStorageConfig } from "./supabaseStorage-
|
|
1
|
+
import { i as supabaseDatabase, n as supabaseStorage, r as SupabaseDatabaseConfig, t as SupabaseStorageConfig } from "./supabaseStorage-CkNoVO3F.cjs";
|
|
2
2
|
export { type SupabaseDatabaseConfig, type SupabaseStorageConfig, supabaseDatabase, supabaseStorage };
|
package/dist/edge.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as supabaseDatabase, n as supabaseStorage, r as SupabaseDatabaseConfig, t as SupabaseStorageConfig } from "./supabaseStorage-
|
|
1
|
+
import { i as supabaseDatabase, n as supabaseStorage, r as SupabaseDatabaseConfig, t as SupabaseStorageConfig } from "./supabaseStorage-CkNoVO3F.mjs";
|
|
2
2
|
export { type SupabaseDatabaseConfig, type SupabaseStorageConfig, supabaseDatabase, supabaseStorage };
|
package/dist/edge.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as supabaseDatabase, t as supabaseStorage } from "./supabaseStorage-
|
|
1
|
+
import { n as supabaseDatabase, t as supabaseStorage } from "./supabaseStorage-CKYtf3yW.mjs";
|
|
2
2
|
export { supabaseDatabase, supabaseStorage };
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_supabaseStorage = require("./supabaseStorage-
|
|
2
|
+
const require_supabaseStorage = require("./supabaseStorage-BJTIWSwx.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.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as supabaseStorage, r as SupabaseDatabaseConfig, t as SupabaseStorageConfig } from "./supabaseStorage-
|
|
1
|
+
import { n as supabaseStorage, r as SupabaseDatabaseConfig, t as SupabaseStorageConfig } from "./supabaseStorage-CkNoVO3F.cjs";
|
|
2
2
|
import { ToolingDatabase } from "@hot-updater/plugin-core";
|
|
3
3
|
//#region src/supabaseMigration.d.ts
|
|
4
4
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as supabaseStorage, r as SupabaseDatabaseConfig, t as SupabaseStorageConfig } from "./supabaseStorage-
|
|
1
|
+
import { n as supabaseStorage, r as SupabaseDatabaseConfig, t as SupabaseStorageConfig } from "./supabaseStorage-CkNoVO3F.mjs";
|
|
2
2
|
import { ToolingDatabase } from "@hot-updater/plugin-core";
|
|
3
3
|
//#region src/supabaseMigration.d.ts
|
|
4
4
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as SUPABASE_TABLE_PREFIX, n as supabaseDatabase$1, r as SUPABASE_APPLY_FUNCTION, t as supabaseStorage } from "./supabaseStorage-
|
|
1
|
+
import { i as SUPABASE_TABLE_PREFIX, n as supabaseDatabase$1, r as SUPABASE_APPLY_FUNCTION, t as supabaseStorage } from "./supabaseStorage-CKYtf3yW.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. */
|
|
@@ -154,7 +154,15 @@ const isMissingObjectError = async (error) => {
|
|
|
154
154
|
};
|
|
155
155
|
/** A character Storage rejects in an object name, `?`, or `!`. */
|
|
156
156
|
const ESCAPED_CHARACTER = /[^A-Za-z0-9_/.*'() &$=@;:+,-]/gu;
|
|
157
|
+
const ESCAPE_SEQUENCE = /!([0-9A-F]{2})/gu;
|
|
157
158
|
const utf8 = new TextEncoder();
|
|
159
|
+
const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
|
|
160
|
+
/** Objects Storage lists a page at a time. */
|
|
161
|
+
const LIST_PAGE_SIZE = 1e3;
|
|
162
|
+
/** Names Storage removes in one request. */
|
|
163
|
+
const DELETE_BATCH_SIZE = 1e3;
|
|
164
|
+
/** The object the Supabase dashboard keeps in a folder it creates. */
|
|
165
|
+
const EMPTY_FOLDER_PLACEHOLDER = ".emptyFolderPlaceholder";
|
|
158
166
|
/**
|
|
159
167
|
* The object name Storage keeps a key under. Storage accepts names of ASCII
|
|
160
168
|
* letters, digits and `_/!.*'() &$=@;:+,?-` only, and supabase-js puts a name
|
|
@@ -165,9 +173,38 @@ const utf8 = new TextEncoder();
|
|
|
165
173
|
* Deploy's keys, such as `<bundleId>/manifest.json`, are stored as they are.
|
|
166
174
|
*/
|
|
167
175
|
const toObjectName = (key) => key.replace(ESCAPED_CHARACTER, (character) => Array.from(utf8.encode(character), (byte) => `!${byte.toString(16).toUpperCase().padStart(2, "0")}`).join(""));
|
|
176
|
+
/** The key `toObjectName` stored under `name`, or null for a name it never makes. */
|
|
177
|
+
const fromObjectName = (name) => {
|
|
178
|
+
const bytes = [];
|
|
179
|
+
const parts = [];
|
|
180
|
+
const flush = () => {
|
|
181
|
+
if (bytes.length === 0) return true;
|
|
182
|
+
try {
|
|
183
|
+
parts.push(utf8Decoder.decode(new Uint8Array(bytes)));
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
bytes.length = 0;
|
|
188
|
+
return true;
|
|
189
|
+
};
|
|
190
|
+
let index = 0;
|
|
191
|
+
for (const match of name.matchAll(ESCAPE_SEQUENCE)) {
|
|
192
|
+
if (match.index !== index) {
|
|
193
|
+
if (!flush()) return null;
|
|
194
|
+
parts.push(name.slice(index, match.index));
|
|
195
|
+
}
|
|
196
|
+
bytes.push(Number.parseInt(match[1], 16));
|
|
197
|
+
index = match.index + match[0].length;
|
|
198
|
+
}
|
|
199
|
+
if (!flush()) return null;
|
|
200
|
+
parts.push(name.slice(index));
|
|
201
|
+
const key = parts.join("");
|
|
202
|
+
return toObjectName(key) === name ? key : null;
|
|
203
|
+
};
|
|
168
204
|
const supabaseStorage = (config) => {
|
|
169
205
|
const bucket = (0, _supabase_supabase_js.createClient)(config.supabaseUrl, resolveSupabaseServiceRoleKey(config)).storage.from(config.bucketName);
|
|
170
206
|
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
207
|
+
const basePrefix = getStorageKey() ? `${getStorageKey()}/` : "";
|
|
171
208
|
const resolveSignedUrl = createSupabaseSignedUrlBatcher({
|
|
172
209
|
createSignedUrls: (_bucketName, keys, expiresIn) => bucket.createSignedUrls(keys, expiresIn),
|
|
173
210
|
expiresIn: config.signedUrlExpiresIn ?? 3600,
|
|
@@ -224,6 +261,51 @@ const supabaseStorage = (config) => {
|
|
|
224
261
|
const { error } = await bucket.remove([toObjectName(key)]);
|
|
225
262
|
if (error && !isNotFoundError(error)) throw new Error(`Failed to delete storage object: ${error.message}`);
|
|
226
263
|
return { deleted: true };
|
|
264
|
+
},
|
|
265
|
+
async listObjects(prefix) {
|
|
266
|
+
const objects = [];
|
|
267
|
+
const folder = toObjectName(getStorageKey(prefix ?? ""));
|
|
268
|
+
let cursor;
|
|
269
|
+
do {
|
|
270
|
+
const { data, error } = await bucket.listV2({
|
|
271
|
+
prefix: folder ? `${folder}/` : "",
|
|
272
|
+
limit: LIST_PAGE_SIZE,
|
|
273
|
+
...cursor === void 0 ? {} : { cursor }
|
|
274
|
+
});
|
|
275
|
+
if (error) throw new Error(`Failed to list storage objects: ${error.message}`);
|
|
276
|
+
for (const entry of data.objects) {
|
|
277
|
+
const name = entry.key ?? entry.name;
|
|
278
|
+
if (name.split("/").at(-1) === EMPTY_FOLDER_PLACEHOLDER) continue;
|
|
279
|
+
const key = fromObjectName(name);
|
|
280
|
+
if (key === null || !key.startsWith(basePrefix)) continue;
|
|
281
|
+
let storageUri;
|
|
282
|
+
try {
|
|
283
|
+
storageUri = (0, _hot_updater_plugin_core.createStorageUri)({
|
|
284
|
+
protocol: "supabase-storage",
|
|
285
|
+
bucket: config.bucketName,
|
|
286
|
+
key
|
|
287
|
+
});
|
|
288
|
+
} catch {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const updatedAt = entry.updated_at ?? entry.created_at;
|
|
292
|
+
objects.push({
|
|
293
|
+
key: key.slice(basePrefix.length),
|
|
294
|
+
...updatedAt ? { lastModifiedAt: new Date(updatedAt) } : {},
|
|
295
|
+
size: Number(entry.metadata?.size ?? 0),
|
|
296
|
+
storageUri
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
cursor = data.hasNext ? data.nextCursor : void 0;
|
|
300
|
+
} while (cursor !== void 0);
|
|
301
|
+
return objects;
|
|
302
|
+
},
|
|
303
|
+
async deleteObjects(relativeKeys) {
|
|
304
|
+
const names = relativeKeys.map((key) => toObjectName(getStorageKey(key)));
|
|
305
|
+
for (let offset = 0; offset < names.length; offset += DELETE_BATCH_SIZE) {
|
|
306
|
+
const { error } = await bucket.remove(names.slice(offset, offset + DELETE_BATCH_SIZE));
|
|
307
|
+
if (error && !isNotFoundError(error)) throw new Error(`Failed to delete storage objects: ${error.message}`);
|
|
308
|
+
}
|
|
227
309
|
}
|
|
228
310
|
});
|
|
229
311
|
};
|
|
@@ -154,7 +154,15 @@ const isMissingObjectError = async (error) => {
|
|
|
154
154
|
};
|
|
155
155
|
/** A character Storage rejects in an object name, `?`, or `!`. */
|
|
156
156
|
const ESCAPED_CHARACTER = /[^A-Za-z0-9_/.*'() &$=@;:+,-]/gu;
|
|
157
|
+
const ESCAPE_SEQUENCE = /!([0-9A-F]{2})/gu;
|
|
157
158
|
const utf8 = new TextEncoder();
|
|
159
|
+
const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
|
|
160
|
+
/** Objects Storage lists a page at a time. */
|
|
161
|
+
const LIST_PAGE_SIZE = 1e3;
|
|
162
|
+
/** Names Storage removes in one request. */
|
|
163
|
+
const DELETE_BATCH_SIZE = 1e3;
|
|
164
|
+
/** The object the Supabase dashboard keeps in a folder it creates. */
|
|
165
|
+
const EMPTY_FOLDER_PLACEHOLDER = ".emptyFolderPlaceholder";
|
|
158
166
|
/**
|
|
159
167
|
* The object name Storage keeps a key under. Storage accepts names of ASCII
|
|
160
168
|
* letters, digits and `_/!.*'() &$=@;:+,?-` only, and supabase-js puts a name
|
|
@@ -165,9 +173,38 @@ const utf8 = new TextEncoder();
|
|
|
165
173
|
* Deploy's keys, such as `<bundleId>/manifest.json`, are stored as they are.
|
|
166
174
|
*/
|
|
167
175
|
const toObjectName = (key) => key.replace(ESCAPED_CHARACTER, (character) => Array.from(utf8.encode(character), (byte) => `!${byte.toString(16).toUpperCase().padStart(2, "0")}`).join(""));
|
|
176
|
+
/** The key `toObjectName` stored under `name`, or null for a name it never makes. */
|
|
177
|
+
const fromObjectName = (name) => {
|
|
178
|
+
const bytes = [];
|
|
179
|
+
const parts = [];
|
|
180
|
+
const flush = () => {
|
|
181
|
+
if (bytes.length === 0) return true;
|
|
182
|
+
try {
|
|
183
|
+
parts.push(utf8Decoder.decode(new Uint8Array(bytes)));
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
bytes.length = 0;
|
|
188
|
+
return true;
|
|
189
|
+
};
|
|
190
|
+
let index = 0;
|
|
191
|
+
for (const match of name.matchAll(ESCAPE_SEQUENCE)) {
|
|
192
|
+
if (match.index !== index) {
|
|
193
|
+
if (!flush()) return null;
|
|
194
|
+
parts.push(name.slice(index, match.index));
|
|
195
|
+
}
|
|
196
|
+
bytes.push(Number.parseInt(match[1], 16));
|
|
197
|
+
index = match.index + match[0].length;
|
|
198
|
+
}
|
|
199
|
+
if (!flush()) return null;
|
|
200
|
+
parts.push(name.slice(index));
|
|
201
|
+
const key = parts.join("");
|
|
202
|
+
return toObjectName(key) === name ? key : null;
|
|
203
|
+
};
|
|
168
204
|
const supabaseStorage = (config) => {
|
|
169
205
|
const bucket = createClient(config.supabaseUrl, resolveSupabaseServiceRoleKey(config)).storage.from(config.bucketName);
|
|
170
206
|
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
207
|
+
const basePrefix = getStorageKey() ? `${getStorageKey()}/` : "";
|
|
171
208
|
const resolveSignedUrl = createSupabaseSignedUrlBatcher({
|
|
172
209
|
createSignedUrls: (_bucketName, keys, expiresIn) => bucket.createSignedUrls(keys, expiresIn),
|
|
173
210
|
expiresIn: config.signedUrlExpiresIn ?? 3600,
|
|
@@ -224,6 +261,51 @@ const supabaseStorage = (config) => {
|
|
|
224
261
|
const { error } = await bucket.remove([toObjectName(key)]);
|
|
225
262
|
if (error && !isNotFoundError(error)) throw new Error(`Failed to delete storage object: ${error.message}`);
|
|
226
263
|
return { deleted: true };
|
|
264
|
+
},
|
|
265
|
+
async listObjects(prefix) {
|
|
266
|
+
const objects = [];
|
|
267
|
+
const folder = toObjectName(getStorageKey(prefix ?? ""));
|
|
268
|
+
let cursor;
|
|
269
|
+
do {
|
|
270
|
+
const { data, error } = await bucket.listV2({
|
|
271
|
+
prefix: folder ? `${folder}/` : "",
|
|
272
|
+
limit: LIST_PAGE_SIZE,
|
|
273
|
+
...cursor === void 0 ? {} : { cursor }
|
|
274
|
+
});
|
|
275
|
+
if (error) throw new Error(`Failed to list storage objects: ${error.message}`);
|
|
276
|
+
for (const entry of data.objects) {
|
|
277
|
+
const name = entry.key ?? entry.name;
|
|
278
|
+
if (name.split("/").at(-1) === EMPTY_FOLDER_PLACEHOLDER) continue;
|
|
279
|
+
const key = fromObjectName(name);
|
|
280
|
+
if (key === null || !key.startsWith(basePrefix)) continue;
|
|
281
|
+
let storageUri;
|
|
282
|
+
try {
|
|
283
|
+
storageUri = createStorageUri({
|
|
284
|
+
protocol: "supabase-storage",
|
|
285
|
+
bucket: config.bucketName,
|
|
286
|
+
key
|
|
287
|
+
});
|
|
288
|
+
} catch {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const updatedAt = entry.updated_at ?? entry.created_at;
|
|
292
|
+
objects.push({
|
|
293
|
+
key: key.slice(basePrefix.length),
|
|
294
|
+
...updatedAt ? { lastModifiedAt: new Date(updatedAt) } : {},
|
|
295
|
+
size: Number(entry.metadata?.size ?? 0),
|
|
296
|
+
storageUri
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
cursor = data.hasNext ? data.nextCursor : void 0;
|
|
300
|
+
} while (cursor !== void 0);
|
|
301
|
+
return objects;
|
|
302
|
+
},
|
|
303
|
+
async deleteObjects(relativeKeys) {
|
|
304
|
+
const names = relativeKeys.map((key) => toObjectName(getStorageKey(key)));
|
|
305
|
+
for (let offset = 0; offset < names.length; offset += DELETE_BATCH_SIZE) {
|
|
306
|
+
const { error } = await bucket.remove(names.slice(offset, offset + DELETE_BATCH_SIZE));
|
|
307
|
+
if (error && !isNotFoundError(error)) throw new Error(`Failed to delete storage objects: ${error.message}`);
|
|
308
|
+
}
|
|
227
309
|
}
|
|
228
310
|
});
|
|
229
311
|
};
|
|
@@ -23,6 +23,6 @@ type SupabaseStorageConfig = SupabaseServiceRoleConfig & {
|
|
|
23
23
|
/** Signed download URL lifetime in seconds. @default 3600 */
|
|
24
24
|
signedUrlExpiresIn?: number;
|
|
25
25
|
};
|
|
26
|
-
declare const supabaseStorage: (config: SupabaseStorageConfig) => StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
|
|
26
|
+
declare const supabaseStorage: (config: SupabaseStorageConfig) => StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete" | "listObjects" | "deleteObjects">;
|
|
27
27
|
//#endregion
|
|
28
28
|
export { supabaseDatabase as i, supabaseStorage as n, SupabaseDatabaseConfig as r, SupabaseStorageConfig as t };
|
|
@@ -23,6 +23,6 @@ type SupabaseStorageConfig = SupabaseServiceRoleConfig & {
|
|
|
23
23
|
/** Signed download URL lifetime in seconds. @default 3600 */
|
|
24
24
|
signedUrlExpiresIn?: number;
|
|
25
25
|
};
|
|
26
|
-
declare const supabaseStorage: (config: SupabaseStorageConfig) => StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete">;
|
|
26
|
+
declare const supabaseStorage: (config: SupabaseStorageConfig) => StorageAdapterWith<"put" | "get" | "getDownloadUrl" | "exists" | "delete" | "listObjects" | "deleteObjects">;
|
|
27
27
|
//#endregion
|
|
28
28
|
export { supabaseDatabase as i, supabaseStorage as n, SupabaseDatabaseConfig as r, SupabaseStorageConfig as t };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/supabase",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.6",
|
|
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.1.
|
|
55
|
-
"@hot-updater/
|
|
56
|
-
"@hot-updater/
|
|
57
|
-
"@hot-updater/
|
|
54
|
+
"@hot-updater/protocol": "1.1.6",
|
|
55
|
+
"@hot-updater/plugin-core": "1.1.6",
|
|
56
|
+
"@hot-updater/server": "1.1.6",
|
|
57
|
+
"@hot-updater/cli-tools": "1.1.6"
|
|
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/test-utils": "1.1.
|
|
68
|
-
"@hot-updater/
|
|
69
|
-
"@hot-updater/
|
|
67
|
+
"@hot-updater/test-utils": "1.1.6",
|
|
68
|
+
"@hot-updater/postgres": "1.1.6",
|
|
69
|
+
"@hot-updater/mock": "1.1.6"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "tsdown",
|