@hot-updater/vercel 1.0.3 → 1.0.5

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.
@@ -17558,7 +17558,7 @@ const postgres = (config) => {
17558
17558
  };
17559
17559
  //#endregion
17560
17560
  //#region ../../packages/server/dist/version.mjs
17561
- const HOT_UPDATER_SERVER_VERSION = "1.0.3";
17561
+ const HOT_UPDATER_SERVER_VERSION = "1.0.5";
17562
17562
  //#endregion
17563
17563
  //#region ../../packages/server/dist/handlerVersionRoutes.mjs
17564
17564
  const createVersionRouteHandlers = (plugins = []) => {
@@ -53404,10 +53404,10 @@ const storeIdOf = (token) => {
53404
53404
  if (prefix !== "vercel" || kind !== "blob" || rw !== "rw" || !id) throw new Error("vercelBlobStorage needs the store's read-write token (vercel_blob_rw_...).");
53405
53405
  return `store_${id}`;
53406
53406
  };
53407
+ const access = "private";
53407
53408
  const vercelBlobStorage = (config) => {
53408
- const { token, access } = config;
53409
+ const { token } = config;
53409
53410
  const storeId = storeIdOf(token);
53410
- const host = `${storeId.slice(6).toLowerCase()}.${access}.blob.vercel-storage.com`;
53411
53411
  const normalizedBasePath = config.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
53412
53412
  const getStorageKey = createStorageKeyBuilder(normalizedBasePath);
53413
53413
  const expiresIn = config.signedUrlExpiresIn ?? 3600;
@@ -53477,7 +53477,6 @@ const vercelBlobStorage = (config) => {
53477
53477
  async getDownloadUrl({ storageUri }) {
53478
53478
  const { key } = parseAndValidate(storageUri);
53479
53479
  const pathname = toPathname(key);
53480
- if (access === "public") return { url: `https://${host}/${pathname.split("/").map(encodeURIComponent).join("/")}` };
53481
53480
  const validUntil = Date.now() + expiresInMs;
53482
53481
  const { presignedUrl } = await presignUrl(await delegationUntil(validUntil), {
53483
53482
  access,
@@ -53564,8 +53563,6 @@ const requireEnv = (name) => {
53564
53563
  if (!value) throw new Error(`${name} is not set on the Vercel project. Run \`hot-updater init\` again to configure it.`);
53565
53564
  return value;
53566
53565
  };
53567
- const access = requireEnv("HOT_UPDATER_VERCEL_BLOB_ACCESS");
53568
- if (access !== "public" && access !== "private") throw new Error("HOT_UPDATER_VERCEL_BLOB_ACCESS must be \"public\" or \"private\".");
53569
53566
  const pool = new import_lib$1.default.Pool({
53570
53567
  connectionString: requireEnv("HOT_UPDATER_VERCEL_DATABASE_URL"),
53571
53568
  idleTimeoutMillis: 5e3,
@@ -53579,10 +53576,7 @@ const hotUpdater = createHotUpdater({
53579
53576
  apiKeys(),
53580
53577
  remoteConfig()
53581
53578
  ],
53582
- storage: vercelBlobStorage({
53583
- access,
53584
- token: requireEnv("HOT_UPDATER_VERCEL_BLOB_READ_WRITE_TOKEN")
53585
- })
53579
+ storage: vercelBlobStorage({ token: requireEnv("HOT_UPDATER_VERCEL_BLOB_READ_WRITE_TOKEN") })
53586
53580
  });
53587
53581
  const handler = async (request) => withoutEncodingVary(await hotUpdater.handlers.client(request));
53588
53582
  //#endregion
package/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
3
3
  let _vercel_blob = require("@vercel/blob");
4
+ let _hot_updater_postgres = require("@hot-updater/postgres");
4
5
  //#region src/vercelBlobStorage.ts
5
6
  const PROTOCOL = "vercel-blob";
6
7
  const ONE_YEAR_IN_SECONDS = 31536e3;
@@ -61,14 +62,15 @@ const storeIdOf = (token) => {
61
62
  if (prefix !== "vercel" || kind !== "blob" || rw !== "rw" || !id) throw new Error("vercelBlobStorage needs the store's read-write token (vercel_blob_rw_...).");
62
63
  return `store_${id}`;
63
64
  };
65
+ /** A private store's objects: only an API key holder's signed URL reaches them. */
66
+ const access = "private";
64
67
  /**
65
- * Bundles in a Vercel Blob store. A public store's objects download from
66
- * their Blob URL; a private store's from a presigned URL that expires.
68
+ * Bundles in a private Vercel Blob store. Devices download from presigned URLs
69
+ * that expire, so the store's objects never sit at a public address.
67
70
  */
68
71
  const vercelBlobStorage = (config) => {
69
- const { token, access } = config;
72
+ const { token } = config;
70
73
  const storeId = storeIdOf(token);
71
- const host = `${storeId.slice(6).toLowerCase()}.${access}.blob.vercel-storage.com`;
72
74
  const normalizedBasePath = config.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
73
75
  const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(normalizedBasePath);
74
76
  const expiresIn = config.signedUrlExpiresIn ?? 3600;
@@ -139,7 +141,6 @@ const vercelBlobStorage = (config) => {
139
141
  async getDownloadUrl({ storageUri }) {
140
142
  const { key } = parseAndValidate(storageUri);
141
143
  const pathname = toPathname(key);
142
- if (access === "public") return { url: `https://${host}/${pathname.split("/").map(encodeURIComponent).join("/")}` };
143
144
  const validUntil = Date.now() + expiresInMs;
144
145
  const { presignedUrl } = await (0, _vercel_blob.presignUrl)(await delegationUntil(validUntil), {
145
146
  access,
@@ -205,4 +206,10 @@ const vercelBlobStorage = (config) => {
205
206
  });
206
207
  };
207
208
  //#endregion
209
+ Object.defineProperty(exports, "postgres", {
210
+ enumerable: true,
211
+ get: function() {
212
+ return _hot_updater_postgres.postgres;
213
+ }
214
+ });
208
215
  exports.vercelBlobStorage = vercelBlobStorage;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { StorageAdapterWith } from "@hot-updater/plugin-core";
2
+ import { PostgresConfig, postgres } from "@hot-updater/postgres";
2
3
  //#region src/vercelBlobStorage.d.ts
3
4
  interface VercelBlobStorageConfig {
4
5
  /**
@@ -6,21 +7,19 @@ interface VercelBlobStorageConfig {
6
7
  * the `BLOB_READ_WRITE_TOKEN` Vercel adds to a project the store connects to.
7
8
  */
8
9
  token: string;
9
- /** The store's access, which Vercel fixes when it creates the store. */
10
- access: "public" | "private";
11
10
  /** Base path where bundles will be stored in the store. */
12
11
  basePath?: string;
13
12
  /**
14
- * Lifetime of a private store's download URLs in seconds, at most seven
15
- * days, Vercel's limit. @default 3600
13
+ * Lifetime of the download URLs in seconds, at most seven days, Vercel's
14
+ * limit. @default 3600
16
15
  */
17
16
  signedUrlExpiresIn?: number;
18
17
  }
19
18
  type VercelBlobStorageOperations = "put" | "get" | "getDownloadUrl" | "exists" | "delete" | "listObjects" | "deleteObjects";
20
19
  /**
21
- * Bundles in a Vercel Blob store. A public store's objects download from
22
- * their Blob URL; a private store's from a presigned URL that expires.
20
+ * Bundles in a private Vercel Blob store. Devices download from presigned URLs
21
+ * that expire, so the store's objects never sit at a public address.
23
22
  */
24
23
  declare const vercelBlobStorage: (config: VercelBlobStorageConfig) => StorageAdapterWith<VercelBlobStorageOperations>;
25
24
  //#endregion
26
- export { VercelBlobStorageConfig, vercelBlobStorage };
25
+ export { type PostgresConfig, VercelBlobStorageConfig, postgres, vercelBlobStorage };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { StorageAdapterWith } from "@hot-updater/plugin-core";
2
+ import { PostgresConfig, postgres } from "@hot-updater/postgres";
2
3
  //#region src/vercelBlobStorage.d.ts
3
4
  interface VercelBlobStorageConfig {
4
5
  /**
@@ -6,21 +7,19 @@ interface VercelBlobStorageConfig {
6
7
  * the `BLOB_READ_WRITE_TOKEN` Vercel adds to a project the store connects to.
7
8
  */
8
9
  token: string;
9
- /** The store's access, which Vercel fixes when it creates the store. */
10
- access: "public" | "private";
11
10
  /** Base path where bundles will be stored in the store. */
12
11
  basePath?: string;
13
12
  /**
14
- * Lifetime of a private store's download URLs in seconds, at most seven
15
- * days, Vercel's limit. @default 3600
13
+ * Lifetime of the download URLs in seconds, at most seven days, Vercel's
14
+ * limit. @default 3600
16
15
  */
17
16
  signedUrlExpiresIn?: number;
18
17
  }
19
18
  type VercelBlobStorageOperations = "put" | "get" | "getDownloadUrl" | "exists" | "delete" | "listObjects" | "deleteObjects";
20
19
  /**
21
- * Bundles in a Vercel Blob store. A public store's objects download from
22
- * their Blob URL; a private store's from a presigned URL that expires.
20
+ * Bundles in a private Vercel Blob store. Devices download from presigned URLs
21
+ * that expire, so the store's objects never sit at a public address.
23
22
  */
24
23
  declare const vercelBlobStorage: (config: VercelBlobStorageConfig) => StorageAdapterWith<VercelBlobStorageOperations>;
25
24
  //#endregion
26
- export { VercelBlobStorageConfig, vercelBlobStorage };
25
+ export { type PostgresConfig, VercelBlobStorageConfig, postgres, vercelBlobStorage };
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createStorageAdapter, createStorageKeyBuilder, createStorageUri, parseStorageUri } from "@hot-updater/plugin-core";
2
2
  import { BlobNotFoundError, del, get, head, issueSignedToken, list, presignUrl, put } from "@vercel/blob";
3
+ import { postgres } from "@hot-updater/postgres";
3
4
  //#region src/vercelBlobStorage.ts
4
5
  const PROTOCOL = "vercel-blob";
5
6
  const ONE_YEAR_IN_SECONDS = 31536e3;
@@ -60,14 +61,15 @@ const storeIdOf = (token) => {
60
61
  if (prefix !== "vercel" || kind !== "blob" || rw !== "rw" || !id) throw new Error("vercelBlobStorage needs the store's read-write token (vercel_blob_rw_...).");
61
62
  return `store_${id}`;
62
63
  };
64
+ /** A private store's objects: only an API key holder's signed URL reaches them. */
65
+ const access = "private";
63
66
  /**
64
- * Bundles in a Vercel Blob store. A public store's objects download from
65
- * their Blob URL; a private store's from a presigned URL that expires.
67
+ * Bundles in a private Vercel Blob store. Devices download from presigned URLs
68
+ * that expire, so the store's objects never sit at a public address.
66
69
  */
67
70
  const vercelBlobStorage = (config) => {
68
- const { token, access } = config;
71
+ const { token } = config;
69
72
  const storeId = storeIdOf(token);
70
- const host = `${storeId.slice(6).toLowerCase()}.${access}.blob.vercel-storage.com`;
71
73
  const normalizedBasePath = config.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
72
74
  const getStorageKey = createStorageKeyBuilder(normalizedBasePath);
73
75
  const expiresIn = config.signedUrlExpiresIn ?? 3600;
@@ -138,7 +140,6 @@ const vercelBlobStorage = (config) => {
138
140
  async getDownloadUrl({ storageUri }) {
139
141
  const { key } = parseAndValidate(storageUri);
140
142
  const pathname = toPathname(key);
141
- if (access === "public") return { url: `https://${host}/${pathname.split("/").map(encodeURIComponent).join("/")}` };
142
143
  const validUntil = Date.now() + expiresInMs;
143
144
  const { presignedUrl } = await presignUrl(await delegationUntil(validUntil), {
144
145
  access,
@@ -204,4 +205,4 @@ const vercelBlobStorage = (config) => {
204
205
  });
205
206
  };
206
207
  //#endregion
207
- export { vercelBlobStorage };
208
+ export { postgres, vercelBlobStorage };
@@ -6276,14 +6276,10 @@ const getConfigScaffold = (build) => {
6276
6276
  }],
6277
6277
  configString: `vercelBlobStorage({
6278
6278
  token: process.env.HOT_UPDATER_VERCEL_BLOB_READ_WRITE_TOKEN!,
6279
- access:
6280
- process.env.HOT_UPDATER_VERCEL_BLOB_ACCESS === "public"
6281
- ? "public"
6282
- : "private",
6283
6279
  })`
6284
6280
  }).setDatabase({
6285
6281
  imports: [{
6286
- pkg: "@hot-updater/postgres",
6282
+ pkg: "@hot-updater/vercel",
6287
6283
  named: ["postgres"]
6288
6284
  }],
6289
6285
  configString: `postgres({
@@ -6310,7 +6306,6 @@ const getConfigScaffold = (build) => {
6310
6306
  const isVercelProjectName = (value) => value !== void 0 && /^[a-z0-9][a-z0-9._-]{0,99}$/.test(value) && !value.includes("---");
6311
6307
  /** A Blob store name: letters, digits, `_`, and `-`. */
6312
6308
  const isVercelBlobStoreName = (value) => value !== void 0 && /^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/.test(value);
6313
- const isVercelBlobAccess = (value) => value === "public" || value === "private";
6314
6309
  const isPostgresUrl = (value) => {
6315
6310
  if (value === void 0) return false;
6316
6311
  try {
@@ -6363,16 +6358,6 @@ const initProvider = {
6363
6358
  help: "Blob store ID (store_...), saved by init",
6364
6359
  optional: true
6365
6360
  },
6366
- blobAccess: {
6367
- envKey: "HOT_UPDATER_VERCEL_BLOB_ACCESS",
6368
- help: "The Blob store's access: private (the default) or public",
6369
- optional: true,
6370
- prompt: {
6371
- message: "Make the new Blob store private?",
6372
- type: "confirm"
6373
- },
6374
- validate: isVercelBlobAccess
6375
- },
6376
6361
  blobReadWriteToken: {
6377
6362
  envKey: "HOT_UPDATER_VERCEL_BLOB_READ_WRITE_TOKEN",
6378
6363
  help: "The Blob store's read-write token, which init reads from the project",
@@ -6791,7 +6776,6 @@ const inputs = initProvider.inputs;
6791
6776
  const BLOB_ENV_PREFIX = "HOT_UPDATER_VERCEL_BLOB";
6792
6777
  const BLOB_TOKEN_ENV = inputs.blobReadWriteToken.envKey;
6793
6778
  const DATABASE_URL_ENV = inputs.databaseUrl.envKey;
6794
- const BLOB_ACCESS_ENV = inputs.blobAccess.envKey;
6795
6779
  /** Project env vars a Marketplace Postgres database adds, such as Neon's `DATABASE_URL`. */
6796
6780
  const DATABASE_URL_ENV_PATTERN = /(?:^|_)(?:DATABASE_URL|POSTGRES_URL)$/;
6797
6781
  const cancelled = (value) => _hot_updater_cli_tools.p.isCancel(value);
@@ -6849,9 +6833,13 @@ const resolveProjectName = async (savedName, nonInteractive) => {
6849
6833
  validate: (value) => isVercelProjectName(value) ? void 0 : "Use up to 100 lowercase letters, digits, '.', '_', or '-', without '---'."
6850
6834
  }));
6851
6835
  };
6852
- /** The saved store, or one the user selects or creates; undefined to create `name`. */
6836
+ /**
6837
+ * The saved private store, or one the user selects or creates. Bundles
6838
+ * download only through signed URLs, so a public store is never used.
6839
+ */
6853
6840
  const selectBlobStore = async ({ nonInteractive, savedId, savedName, stores }) => {
6854
- const saved = stores.find((store) => store.id === savedId) ?? (savedId === void 0 ? stores.find((store) => store.name === savedName) : void 0);
6841
+ const privateStores = stores.filter((store) => store.access === "private");
6842
+ const saved = privateStores.find((store) => store.id === savedId) ?? (savedId === void 0 ? privateStores.find((store) => store.name === savedName) : void 0);
6855
6843
  if (nonInteractive) {
6856
6844
  if (saved) return {
6857
6845
  kind: "existing",
@@ -6867,16 +6855,16 @@ const selectBlobStore = async ({ nonInteractive, savedId, savedName, stores }) =
6867
6855
  const createKey = "\0create";
6868
6856
  const selected = exitIfCancelled(await _hot_updater_cli_tools.p.select({
6869
6857
  message: "Select the Blob store for bundles",
6870
- initialValue: saved?.id ?? stores[0]?.id ?? createKey,
6871
- options: [...stores.map((store) => ({
6858
+ initialValue: saved?.id ?? privateStores[0]?.id ?? createKey,
6859
+ options: [...privateStores.map((store) => ({
6872
6860
  value: store.id,
6873
- label: `${store.name} (${store.access}${store.region ? `, ${store.region}` : ""})`
6861
+ label: `${store.name}${store.region ? ` (${store.region})` : ""}`
6874
6862
  })), {
6875
6863
  value: createKey,
6876
6864
  label: "Create a new Blob store"
6877
6865
  }]
6878
6866
  }));
6879
- const store = stores.find((candidate) => candidate.id === selected);
6867
+ const store = privateStores.find((candidate) => candidate.id === selected);
6880
6868
  if (store) return {
6881
6869
  kind: "existing",
6882
6870
  store
@@ -6891,14 +6879,6 @@ const selectBlobStore = async ({ nonInteractive, savedId, savedName, stores }) =
6891
6879
  }))
6892
6880
  };
6893
6881
  };
6894
- const resolveNewStoreAccess = async (savedAccess, nonInteractive) => {
6895
- if (isVercelBlobAccess(savedAccess) && nonInteractive) return savedAccess;
6896
- if (nonInteractive) return "private";
6897
- return exitIfCancelled(await _hot_updater_cli_tools.p.confirm({
6898
- message: inputs.blobAccess.prompt.message,
6899
- initialValue: savedAccess !== "public"
6900
- })) ? "private" : "public";
6901
- };
6902
6882
  /**
6903
6883
  * The store's read-write token: from its connection to the project, which
6904
6884
  * init makes when there is none.
@@ -6917,6 +6897,7 @@ const resolveBlobToken = async (api, project, store) => {
6917
6897
  const env = (await api.listProjectEnv(project.id)).find((candidate) => candidate.key === tokenEnv && candidate.target.includes("production"));
6918
6898
  const token = env === void 0 ? void 0 : await api.getProjectEnvValue(project.id, env.id);
6919
6899
  if (!token?.startsWith("vercel_blob_rw_")) throw new _hot_updater_cli_tools.InitError(`Blob store ${store.name} is connected to project ${project.name} without a read-write token for production. Reconnect it from the project's Storage tab with a read-write token, then rerun init.`);
6900
+ if (`store_${token.split("_")[3]}` !== store.id) throw new _hot_updater_cli_tools.InitError(`The production ${tokenEnv} of project ${project.name} belongs to another Blob store, not ${store.name}. Disconnect that store from the project's Storage tab, then rerun init.`);
6920
6901
  return token;
6921
6902
  };
6922
6903
  /**
@@ -7051,7 +7032,6 @@ const runInit = async ({ build, envFile }) => {
7051
7032
  ...saved.blobStoreId ? { savedId: saved.blobStoreId } : {},
7052
7033
  ...saved.blobStoreName ? { savedName: saved.blobStoreName } : {}
7053
7034
  });
7054
- const newStoreAccess = storeSelection.kind === "create" ? await resolveNewStoreAccess(saved.blobAccess, nonInteractive) : void 0;
7055
7035
  let databaseUrl = nonInteractive ? saved.databaseUrl : void 0;
7056
7036
  if (nonInteractive && !isPostgresUrl(databaseUrl)) throw new _hot_updater_cli_tools.InitError(`${DATABASE_URL_ENV} must be a postgres:// or postgresql:// URL.`);
7057
7037
  const project = existingProject ?? await (async () => {
@@ -7073,23 +7053,21 @@ const runInit = async ({ build, envFile }) => {
7073
7053
  store = storeSelection.store;
7074
7054
  _hot_updater_cli_tools.p.log.info(`Selected Blob store: ${store.name}`);
7075
7055
  } else {
7076
- const access = newStoreAccess ?? "private";
7077
7056
  const { id } = await api.createBlobStore({
7078
- access,
7057
+ access: "private",
7079
7058
  name: storeSelection.name
7080
7059
  });
7081
7060
  store = {
7082
- access,
7061
+ access: "private",
7083
7062
  connections: [],
7084
7063
  id,
7085
7064
  name: storeSelection.name,
7086
7065
  region: void 0
7087
7066
  };
7088
- _hot_updater_cli_tools.p.log.info(`Created Blob store: ${store.name} (${access})`);
7067
+ _hot_updater_cli_tools.p.log.info(`Created Blob store: ${store.name} (private)`);
7089
7068
  }
7090
7069
  const blobToken = await resolveBlobToken(api, project, store);
7091
7070
  const resolvedInputs = {
7092
- blobAccess: store.access,
7093
7071
  blobReadWriteToken: blobToken,
7094
7072
  blobStoreId: store.id,
7095
7073
  blobStoreName: store.name,
@@ -7124,7 +7102,6 @@ const runInit = async ({ build, envFile }) => {
7124
7102
  title: "Setting the Function's environment variables...",
7125
7103
  task: async () => {
7126
7104
  await api.setProductionEnv(project.id, DATABASE_URL_ENV, databaseUrl);
7127
- await api.setProductionEnv(project.id, BLOB_ACCESS_ENV, store.access);
7128
7105
  await syncFunctionBlobToken(api, project, store, blobToken);
7129
7106
  return "Set the Function's environment variables";
7130
7107
  }
@@ -43,16 +43,6 @@ declare const initProvider: {
43
43
  readonly help: "Blob store ID (store_...), saved by init";
44
44
  readonly optional: true;
45
45
  };
46
- readonly blobAccess: {
47
- readonly envKey: "HOT_UPDATER_VERCEL_BLOB_ACCESS";
48
- readonly help: "The Blob store's access: private (the default) or public";
49
- readonly optional: true;
50
- readonly prompt: {
51
- readonly message: "Make the new Blob store private?";
52
- readonly type: "confirm";
53
- };
54
- readonly validate: (value: string | undefined) => value is "public" | "private";
55
- };
56
46
  readonly blobReadWriteToken: {
57
47
  readonly envKey: "HOT_UPDATER_VERCEL_BLOB_READ_WRITE_TOKEN";
58
48
  readonly help: "The Blob store's read-write token, which init reads from the project";
@@ -43,16 +43,6 @@ declare const initProvider: {
43
43
  readonly help: "Blob store ID (store_...), saved by init";
44
44
  readonly optional: true;
45
45
  };
46
- readonly blobAccess: {
47
- readonly envKey: "HOT_UPDATER_VERCEL_BLOB_ACCESS";
48
- readonly help: "The Blob store's access: private (the default) or public";
49
- readonly optional: true;
50
- readonly prompt: {
51
- readonly message: "Make the new Blob store private?";
52
- readonly type: "confirm";
53
- };
54
- readonly validate: (value: string | undefined) => value is "public" | "private";
55
- };
56
46
  readonly blobReadWriteToken: {
57
47
  readonly envKey: "HOT_UPDATER_VERCEL_BLOB_READ_WRITE_TOKEN";
58
48
  readonly help: "The Blob store's read-write token, which init reads from the project";
@@ -6270,14 +6270,10 @@ const getConfigScaffold = (build) => {
6270
6270
  }],
6271
6271
  configString: `vercelBlobStorage({
6272
6272
  token: process.env.HOT_UPDATER_VERCEL_BLOB_READ_WRITE_TOKEN!,
6273
- access:
6274
- process.env.HOT_UPDATER_VERCEL_BLOB_ACCESS === "public"
6275
- ? "public"
6276
- : "private",
6277
6273
  })`
6278
6274
  }).setDatabase({
6279
6275
  imports: [{
6280
- pkg: "@hot-updater/postgres",
6276
+ pkg: "@hot-updater/vercel",
6281
6277
  named: ["postgres"]
6282
6278
  }],
6283
6279
  configString: `postgres({
@@ -6304,7 +6300,6 @@ const getConfigScaffold = (build) => {
6304
6300
  const isVercelProjectName = (value) => value !== void 0 && /^[a-z0-9][a-z0-9._-]{0,99}$/.test(value) && !value.includes("---");
6305
6301
  /** A Blob store name: letters, digits, `_`, and `-`. */
6306
6302
  const isVercelBlobStoreName = (value) => value !== void 0 && /^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/.test(value);
6307
- const isVercelBlobAccess = (value) => value === "public" || value === "private";
6308
6303
  const isPostgresUrl = (value) => {
6309
6304
  if (value === void 0) return false;
6310
6305
  try {
@@ -6357,16 +6352,6 @@ const initProvider = {
6357
6352
  help: "Blob store ID (store_...), saved by init",
6358
6353
  optional: true
6359
6354
  },
6360
- blobAccess: {
6361
- envKey: "HOT_UPDATER_VERCEL_BLOB_ACCESS",
6362
- help: "The Blob store's access: private (the default) or public",
6363
- optional: true,
6364
- prompt: {
6365
- message: "Make the new Blob store private?",
6366
- type: "confirm"
6367
- },
6368
- validate: isVercelBlobAccess
6369
- },
6370
6355
  blobReadWriteToken: {
6371
6356
  envKey: "HOT_UPDATER_VERCEL_BLOB_READ_WRITE_TOKEN",
6372
6357
  help: "The Blob store's read-write token, which init reads from the project",
@@ -6785,7 +6770,6 @@ const inputs = initProvider.inputs;
6785
6770
  const BLOB_ENV_PREFIX = "HOT_UPDATER_VERCEL_BLOB";
6786
6771
  const BLOB_TOKEN_ENV = inputs.blobReadWriteToken.envKey;
6787
6772
  const DATABASE_URL_ENV = inputs.databaseUrl.envKey;
6788
- const BLOB_ACCESS_ENV = inputs.blobAccess.envKey;
6789
6773
  /** Project env vars a Marketplace Postgres database adds, such as Neon's `DATABASE_URL`. */
6790
6774
  const DATABASE_URL_ENV_PATTERN = /(?:^|_)(?:DATABASE_URL|POSTGRES_URL)$/;
6791
6775
  const cancelled = (value) => p.isCancel(value);
@@ -6843,9 +6827,13 @@ const resolveProjectName = async (savedName, nonInteractive) => {
6843
6827
  validate: (value) => isVercelProjectName(value) ? void 0 : "Use up to 100 lowercase letters, digits, '.', '_', or '-', without '---'."
6844
6828
  }));
6845
6829
  };
6846
- /** The saved store, or one the user selects or creates; undefined to create `name`. */
6830
+ /**
6831
+ * The saved private store, or one the user selects or creates. Bundles
6832
+ * download only through signed URLs, so a public store is never used.
6833
+ */
6847
6834
  const selectBlobStore = async ({ nonInteractive, savedId, savedName, stores }) => {
6848
- const saved = stores.find((store) => store.id === savedId) ?? (savedId === void 0 ? stores.find((store) => store.name === savedName) : void 0);
6835
+ const privateStores = stores.filter((store) => store.access === "private");
6836
+ const saved = privateStores.find((store) => store.id === savedId) ?? (savedId === void 0 ? privateStores.find((store) => store.name === savedName) : void 0);
6849
6837
  if (nonInteractive) {
6850
6838
  if (saved) return {
6851
6839
  kind: "existing",
@@ -6861,16 +6849,16 @@ const selectBlobStore = async ({ nonInteractive, savedId, savedName, stores }) =
6861
6849
  const createKey = "\0create";
6862
6850
  const selected = exitIfCancelled(await p.select({
6863
6851
  message: "Select the Blob store for bundles",
6864
- initialValue: saved?.id ?? stores[0]?.id ?? createKey,
6865
- options: [...stores.map((store) => ({
6852
+ initialValue: saved?.id ?? privateStores[0]?.id ?? createKey,
6853
+ options: [...privateStores.map((store) => ({
6866
6854
  value: store.id,
6867
- label: `${store.name} (${store.access}${store.region ? `, ${store.region}` : ""})`
6855
+ label: `${store.name}${store.region ? ` (${store.region})` : ""}`
6868
6856
  })), {
6869
6857
  value: createKey,
6870
6858
  label: "Create a new Blob store"
6871
6859
  }]
6872
6860
  }));
6873
- const store = stores.find((candidate) => candidate.id === selected);
6861
+ const store = privateStores.find((candidate) => candidate.id === selected);
6874
6862
  if (store) return {
6875
6863
  kind: "existing",
6876
6864
  store
@@ -6885,14 +6873,6 @@ const selectBlobStore = async ({ nonInteractive, savedId, savedName, stores }) =
6885
6873
  }))
6886
6874
  };
6887
6875
  };
6888
- const resolveNewStoreAccess = async (savedAccess, nonInteractive) => {
6889
- if (isVercelBlobAccess(savedAccess) && nonInteractive) return savedAccess;
6890
- if (nonInteractive) return "private";
6891
- return exitIfCancelled(await p.confirm({
6892
- message: inputs.blobAccess.prompt.message,
6893
- initialValue: savedAccess !== "public"
6894
- })) ? "private" : "public";
6895
- };
6896
6876
  /**
6897
6877
  * The store's read-write token: from its connection to the project, which
6898
6878
  * init makes when there is none.
@@ -6911,6 +6891,7 @@ const resolveBlobToken = async (api, project, store) => {
6911
6891
  const env = (await api.listProjectEnv(project.id)).find((candidate) => candidate.key === tokenEnv && candidate.target.includes("production"));
6912
6892
  const token = env === void 0 ? void 0 : await api.getProjectEnvValue(project.id, env.id);
6913
6893
  if (!token?.startsWith("vercel_blob_rw_")) throw new InitError(`Blob store ${store.name} is connected to project ${project.name} without a read-write token for production. Reconnect it from the project's Storage tab with a read-write token, then rerun init.`);
6894
+ if (`store_${token.split("_")[3]}` !== store.id) throw new InitError(`The production ${tokenEnv} of project ${project.name} belongs to another Blob store, not ${store.name}. Disconnect that store from the project's Storage tab, then rerun init.`);
6914
6895
  return token;
6915
6896
  };
6916
6897
  /**
@@ -7045,7 +7026,6 @@ const runInit = async ({ build, envFile }) => {
7045
7026
  ...saved.blobStoreId ? { savedId: saved.blobStoreId } : {},
7046
7027
  ...saved.blobStoreName ? { savedName: saved.blobStoreName } : {}
7047
7028
  });
7048
- const newStoreAccess = storeSelection.kind === "create" ? await resolveNewStoreAccess(saved.blobAccess, nonInteractive) : void 0;
7049
7029
  let databaseUrl = nonInteractive ? saved.databaseUrl : void 0;
7050
7030
  if (nonInteractive && !isPostgresUrl(databaseUrl)) throw new InitError(`${DATABASE_URL_ENV} must be a postgres:// or postgresql:// URL.`);
7051
7031
  const project = existingProject ?? await (async () => {
@@ -7067,23 +7047,21 @@ const runInit = async ({ build, envFile }) => {
7067
7047
  store = storeSelection.store;
7068
7048
  p.log.info(`Selected Blob store: ${store.name}`);
7069
7049
  } else {
7070
- const access = newStoreAccess ?? "private";
7071
7050
  const { id } = await api.createBlobStore({
7072
- access,
7051
+ access: "private",
7073
7052
  name: storeSelection.name
7074
7053
  });
7075
7054
  store = {
7076
- access,
7055
+ access: "private",
7077
7056
  connections: [],
7078
7057
  id,
7079
7058
  name: storeSelection.name,
7080
7059
  region: void 0
7081
7060
  };
7082
- p.log.info(`Created Blob store: ${store.name} (${access})`);
7061
+ p.log.info(`Created Blob store: ${store.name} (private)`);
7083
7062
  }
7084
7063
  const blobToken = await resolveBlobToken(api, project, store);
7085
7064
  const resolvedInputs = {
7086
- blobAccess: store.access,
7087
7065
  blobReadWriteToken: blobToken,
7088
7066
  blobStoreId: store.id,
7089
7067
  blobStoreName: store.name,
@@ -7118,7 +7096,6 @@ const runInit = async ({ build, envFile }) => {
7118
7096
  title: "Setting the Function's environment variables...",
7119
7097
  task: async () => {
7120
7098
  await api.setProductionEnv(project.id, DATABASE_URL_ENV, databaseUrl);
7121
- await api.setProductionEnv(project.id, BLOB_ACCESS_ENV, store.access);
7122
7099
  await syncFunctionBlobToken(api, project, store, blobToken);
7123
7100
  return "Set the Function's environment variables";
7124
7101
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/vercel",
3
3
  "type": "module",
4
- "version": "1.0.3",
4
+ "version": "1.0.5",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -41,11 +41,11 @@
41
41
  "@vercel/cli-auth": "0.3.10",
42
42
  "@vercel/cli-config": "0.3.1",
43
43
  "pg": "8.13.1",
44
- "@hot-updater/plugin-core": "1.0.3",
45
- "@hot-updater/cli-tools": "1.0.3",
46
- "@hot-updater/postgres": "1.0.3",
47
- "@hot-updater/protocol": "1.0.3",
48
- "@hot-updater/server": "1.0.3"
44
+ "@hot-updater/cli-tools": "1.0.5",
45
+ "@hot-updater/postgres": "1.0.5",
46
+ "@hot-updater/protocol": "1.0.5",
47
+ "@hot-updater/plugin-core": "1.0.5",
48
+ "@hot-updater/server": "1.0.5"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^20",
@@ -54,7 +54,7 @@
54
54
  "execa": "9.5.2",
55
55
  "kysely": "0.28.17",
56
56
  "vitest": "4.1.4",
57
- "@hot-updater/test-utils": "1.0.3"
57
+ "@hot-updater/test-utils": "1.0.5"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsdown",