@hot-updater/azure 1.2.0-rc.4 → 1.2.0-rc.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.
@@ -8118,7 +8118,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
8118
8118
  };
8119
8119
  //#endregion
8120
8120
  //#region ../../packages/server/dist/version.mjs
8121
- const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.4";
8121
+ const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.5";
8122
8122
  //#endregion
8123
8123
  //#region ../../packages/server/dist/handlerVersionRoutes.mjs
8124
8124
  const createVersionRouteHandlers = (plugins = []) => {
@@ -57415,12 +57415,19 @@ const PROTOCOL = "azblob";
57415
57415
  const SAS_EXPIRES_IN_MS = 36e5;
57416
57416
  const DELEGATION_KEY_LIFETIME_MS = 864e5;
57417
57417
  const isNotFound = (error) => error instanceof RestError && error.statusCode === 404;
57418
- const serviceOf = (config) => "connectionString" in config ? BlobServiceClient.fromConnectionString(config.connectionString) : new BlobServiceClient(config.accountUrl, config.credential);
57418
+ const serviceOf = async (config) => {
57419
+ if (!("connectionString" in config)) return new BlobServiceClient(config.accountUrl, config.credential);
57420
+ const { connectionString } = config;
57421
+ return BlobServiceClient.fromConnectionString(typeof connectionString === "function" ? await connectionString() : connectionString);
57422
+ };
57419
57423
  function azureBlobStorage(config) {
57420
57424
  const { containerName, basePath, getDownloadUrl } = config;
57421
57425
  let client;
57422
- const service = () => client ??= serviceOf(config);
57423
- const container = () => service().getContainerClient(containerName);
57426
+ const service = () => client ??= serviceOf(config).catch((error) => {
57427
+ client = void 0;
57428
+ throw error;
57429
+ });
57430
+ const container = async () => (await service()).getContainerClient(containerName);
57424
57431
  const normalizedBasePath = basePath?.replace(/^\/+|\/+$/g, "") ?? "";
57425
57432
  const getStorageKey = createStorageKeyBuilder(normalizedBasePath);
57426
57433
  const getListPrefix = (prefix = "") => {
@@ -57451,7 +57458,7 @@ function azureBlobStorage(config) {
57451
57458
  const delegationKey = () => {
57452
57459
  const now = Date.now();
57453
57460
  if (delegation === void 0 || delegation.renewAt <= now) {
57454
- const key = service().getUserDelegationKey(/* @__PURE__ */ new Date(now - 3e5), new Date(now + DELEGATION_KEY_LIFETIME_MS));
57461
+ const key = service().then((blobService) => blobService.getUserDelegationKey(/* @__PURE__ */ new Date(now - 3e5), new Date(now + DELEGATION_KEY_LIFETIME_MS)));
57455
57462
  key.catch(() => {
57456
57463
  delegation = void 0;
57457
57464
  });
@@ -57463,7 +57470,7 @@ function azureBlobStorage(config) {
57463
57470
  return delegation.key;
57464
57471
  };
57465
57472
  const signedUrl = async (key) => {
57466
- const blob = container().getBlobClient(key);
57473
+ const blob = (await container()).getBlobClient(key);
57467
57474
  const expiresOn = new Date(Date.now() + SAS_EXPIRES_IN_MS);
57468
57475
  const permissions = BlobSASPermissions.parse("r");
57469
57476
  if (blob.credential instanceof StorageSharedKeyCredential) return blob.generateSasUrl({
@@ -57484,7 +57491,8 @@ function azureBlobStorage(config) {
57484
57491
  protocol: PROTOCOL,
57485
57492
  async listObjects(prefix) {
57486
57493
  const objects = [];
57487
- for await (const blob of container().listBlobsFlat({ prefix: getListPrefix(prefix) })) {
57494
+ const blobs = await container();
57495
+ for await (const blob of blobs.listBlobsFlat({ prefix: getListPrefix(prefix) })) {
57488
57496
  const storageUri = getListedStorageUri(blob.name);
57489
57497
  if (storageUri === null) continue;
57490
57498
  objects.push({
@@ -57501,7 +57509,8 @@ function azureBlobStorage(config) {
57501
57509
  for (let offset = 0; offset < keys.length; offset += 256) {
57502
57510
  const batch = keys.slice(offset, offset + 256);
57503
57511
  if (batch.length === 0) continue;
57504
- const failed = (await container().getBlobBatchClient().deleteBlobs(batch.map((key) => container().getBlobClient(key)))).subResponses.filter(({ status }) => status >= 300 && status !== 404);
57512
+ const blobs = await container();
57513
+ const failed = (await blobs.getBlobBatchClient().deleteBlobs(batch.map((key) => blobs.getBlobClient(key)))).subResponses.filter(({ status }) => status >= 300 && status !== 404);
57505
57514
  if (failed.length > 0) throw new Error(`Failed to delete ${failed.length} Azure blob(s).`);
57506
57515
  }
57507
57516
  },
@@ -57511,7 +57520,7 @@ function azureBlobStorage(config) {
57511
57520
  blobContentType: contentType,
57512
57521
  blobCacheControl: "max-age=31536000"
57513
57522
  } };
57514
- const blob = container().getBlockBlobClient(storageKey);
57523
+ const blob = (await container()).getBlockBlobClient(storageKey);
57515
57524
  if (contentLength === void 0) await blob.uploadData(new Uint8Array(await new Response(body).arrayBuffer()), options);
57516
57525
  else {
57517
57526
  const { Readable } = await import("node:stream");
@@ -57540,7 +57549,7 @@ function azureBlobStorage(config) {
57540
57549
  async get({ storageUri }) {
57541
57550
  const { key } = parseAndValidate(storageUri);
57542
57551
  try {
57543
- const download = await container().getBlobClient(key).download();
57552
+ const download = await (await container()).getBlobClient(key).download();
57544
57553
  const body = download.readableStreamBody;
57545
57554
  if (body === void 0) return { response: null };
57546
57555
  const headers = new Headers();
@@ -57560,11 +57569,11 @@ function azureBlobStorage(config) {
57560
57569
  },
57561
57570
  async exists({ storageUri }) {
57562
57571
  const { key } = parseAndValidate(storageUri);
57563
- return { exists: await container().getBlobClient(key).exists() };
57572
+ return { exists: await (await container()).getBlobClient(key).exists() };
57564
57573
  },
57565
57574
  async delete({ storageUri }) {
57566
57575
  const { key } = parseAndValidate(storageUri);
57567
- await container().getBlobClient(key).deleteIfExists();
57576
+ await (await container()).getBlobClient(key).deleteIfExists();
57568
57577
  return { deleted: true };
57569
57578
  }
57570
57579
  });
@@ -70857,18 +70866,21 @@ const isWideAdd = (op) => op.type === "add" && Object.keys(op.by).length + (op.e
70857
70866
  const emulatorAborted = (error) => statusOf(error) === 500 && /PostgresError\(SqlState\(E(25P02|40001|40P01)\)/.test(String(error instanceof Error ? error.message : error));
70858
70867
  const createCosmosStore = ({ client, databaseId, containerId, engine, nativePageSize }) => {
70859
70868
  let opened;
70860
- const open = () => opened ??= (() => {
70861
- const account = typeof client === "function" ? client() : client;
70869
+ const open = () => opened ??= (async () => {
70870
+ const account = typeof client === "function" ? await client() : client;
70862
70871
  return {
70863
70872
  client: account,
70864
70873
  container: account.database(databaseId).container(containerId)
70865
70874
  };
70866
- })();
70867
- const container = () => open().container;
70875
+ })().catch((error) => {
70876
+ opened = void 0;
70877
+ throw error;
70878
+ });
70879
+ const container = async () => (await open()).container;
70868
70880
  const readStates = async (ids, rowIds) => {
70869
70881
  const states = /* @__PURE__ */ new Map();
70870
70882
  for (const [group, projection] of [[ids.filter((id) => !rowIds.has(id)), ""], [ids.filter((id) => rowIds.has(id)), ", c.row"]]) for (let at = 0; at < group.length; at += GET_BATCH) {
70871
- const { resources } = await container().items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
70883
+ const { resources } = await (await container()).items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
70872
70884
  for (const { id, etag, v, expireAt, row } of resources) states.set(id, {
70873
70885
  etag,
70874
70886
  v,
@@ -71000,7 +71012,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
71000
71012
  const found = /* @__PURE__ */ new Map();
71001
71013
  const ids = [...new Set(keys.map(cosmosItemId))];
71002
71014
  for (let at = 0; at < ids.length; at += GET_BATCH) {
71003
- const { resources } = await container().items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
71015
+ const { resources } = await (await container()).items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
71004
71016
  for (const resource of resources) found.set(resource.id, toRow(resource.row));
71005
71017
  }
71006
71018
  return keys.map((key) => found.get(cosmosItemId(key)) ?? null);
@@ -71036,7 +71048,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
71036
71048
  value: after
71037
71049
  });
71038
71050
  }
71039
- const { resources } = await container().items.query({
71051
+ const { resources } = await (await container()).items.query({
71040
71052
  query: `SELECT TOP @size c.sk, c.row FROM c WHERE ${filters.join(" AND ")} ORDER BY c.pk ${order === "asc" ? "ASC" : "DESC"}, c.sk ${order === "asc" ? "ASC" : "DESC"}`,
71041
71053
  parameters
71042
71054
  }, {
@@ -71086,7 +71098,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
71086
71098
  if (planned.length > BATCH_OPERATIONS) throw new Error(`A Cosmos DB batch takes ${BATCH_OPERATIONS} operations; this write needs ${planned.length}.`);
71087
71099
  let results;
71088
71100
  try {
71089
- const response = await container().items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
71101
+ const response = await (await container()).items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
71090
71102
  if (response.result === void 0) throw Object.assign(/* @__PURE__ */ new Error(`Cosmos DB refused the batch with status ${response.code}`), { code: response.code });
71091
71103
  results = response.result;
71092
71104
  } catch (error) {
@@ -71119,7 +71131,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
71119
71131
  async deleteConsumed(keys) {
71120
71132
  let remaining = [...new Set(keys.map(cosmosItemId))];
71121
71133
  for (let attempt = 0; remaining.length > 0; attempt += 1) {
71122
- const failed = (await container().items.executeBulkOperations(remaining.map((id) => ({
71134
+ const failed = (await (await container()).items.executeBulkOperations(remaining.map((id) => ({
71123
71135
  operationType: "Delete",
71124
71136
  id,
71125
71137
  partitionKey: COSMOS_PARTITION
@@ -71138,8 +71150,8 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
71138
71150
  }
71139
71151
  },
71140
71152
  migrations: { async apply() {
71141
- if (engine !== void 0) await assertCosmosContainerReusable(container(), engine);
71142
- const { database } = await open().client.databases.createIfNotExists({ id: databaseId });
71153
+ if (engine !== void 0) await assertCosmosContainerReusable(await container(), engine);
71154
+ const { database } = await (await open()).client.databases.createIfNotExists({ id: databaseId });
71143
71155
  const { resource } = await database.containers.createIfNotExists({
71144
71156
  id: containerId,
71145
71157
  partitionKey: { paths: [COSMOS_PARTITION_KEY_PATH] },
@@ -71176,8 +71188,12 @@ const adapterOf = (config) => {
71176
71188
  const { databaseId = AZURE_COSMOS_V1_DATABASE, containerId = AZURE_COSMOS_V1_CONTAINER, aggregateBatching: _, ...connection } = config;
71177
71189
  if (connection.consistencyLevel !== void 0 && connection.consistencyLevel !== "Strong") throw new Error(`cosmosDatabase reads with strong consistency; consistencyLevel "${String(connection.consistencyLevel)}" is weaker.`);
71178
71190
  let client;
71191
+ const { connectionString } = connection;
71179
71192
  const store = createCosmosStore({
71180
- client: () => client ??= new CosmosClient(connection),
71193
+ client: async () => client ??= new CosmosClient({
71194
+ ...connection,
71195
+ connectionString: typeof connectionString === "function" ? await connectionString() : connectionString
71196
+ }),
71181
71197
  databaseId,
71182
71198
  containerId,
71183
71199
  engine: currentCosmosEngine()
@@ -71189,7 +71205,7 @@ const adapterOf = (config) => {
71189
71205
  };
71190
71206
  const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
71191
71207
  const accountHostOf = ({ endpoint, connectionString }) => {
71192
- const url = endpoint ?? connectionString?.match(/AccountEndpoint=([^;]+)/iu)?.[1];
71208
+ const url = endpoint ?? (typeof connectionString === "string" ? connectionString.match(/AccountEndpoint=([^;]+)/iu)?.[1] : void 0);
71193
71209
  try {
71194
71210
  return url === void 0 ? void 0 : new URL(url).hostname;
71195
71211
  } catch {
package/dist/index.cjs CHANGED
@@ -11,7 +11,11 @@ const SAS_EXPIRES_IN_MS = 36e5;
11
11
  /** A user delegation key signs SAS URLs for up to seven days; it is renewed well before. */
12
12
  const DELEGATION_KEY_LIFETIME_MS = 864e5;
13
13
  const isNotFound = (error) => error instanceof _azure_storage_blob.RestError && error.statusCode === 404;
14
- const serviceOf = (config) => "connectionString" in config ? _azure_storage_blob.BlobServiceClient.fromConnectionString(config.connectionString) : new _azure_storage_blob.BlobServiceClient(config.accountUrl, config.credential);
14
+ const serviceOf = async (config) => {
15
+ if (!("connectionString" in config)) return new _azure_storage_blob.BlobServiceClient(config.accountUrl, config.credential);
16
+ const { connectionString } = config;
17
+ return _azure_storage_blob.BlobServiceClient.fromConnectionString(typeof connectionString === "function" ? await connectionString() : connectionString);
18
+ };
15
19
  /**
16
20
  * Bundles in an Azure Blob Storage container. Without `getDownloadUrl`,
17
21
  * devices download through SAS URLs: signed with the account key when the
@@ -21,8 +25,11 @@ const serviceOf = (config) => "connectionString" in config ? _azure_storage_blob
21
25
  function azureBlobStorage(config) {
22
26
  const { containerName, basePath, getDownloadUrl } = config;
23
27
  let client;
24
- const service = () => client ??= serviceOf(config);
25
- const container = () => service().getContainerClient(containerName);
28
+ const service = () => client ??= serviceOf(config).catch((error) => {
29
+ client = void 0;
30
+ throw error;
31
+ });
32
+ const container = async () => (await service()).getContainerClient(containerName);
26
33
  const normalizedBasePath = basePath?.replace(/^\/+|\/+$/g, "") ?? "";
27
34
  const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(normalizedBasePath);
28
35
  const getListPrefix = (prefix = "") => {
@@ -54,7 +61,7 @@ function azureBlobStorage(config) {
54
61
  const delegationKey = () => {
55
62
  const now = Date.now();
56
63
  if (delegation === void 0 || delegation.renewAt <= now) {
57
- const key = service().getUserDelegationKey(/* @__PURE__ */ new Date(now - 3e5), new Date(now + DELEGATION_KEY_LIFETIME_MS));
64
+ const key = service().then((blobService) => blobService.getUserDelegationKey(/* @__PURE__ */ new Date(now - 3e5), new Date(now + DELEGATION_KEY_LIFETIME_MS)));
58
65
  key.catch(() => {
59
66
  delegation = void 0;
60
67
  });
@@ -66,7 +73,7 @@ function azureBlobStorage(config) {
66
73
  return delegation.key;
67
74
  };
68
75
  const signedUrl = async (key) => {
69
- const blob = container().getBlobClient(key);
76
+ const blob = (await container()).getBlobClient(key);
70
77
  const expiresOn = new Date(Date.now() + SAS_EXPIRES_IN_MS);
71
78
  const permissions = _azure_storage_blob.BlobSASPermissions.parse("r");
72
79
  if (blob.credential instanceof _azure_storage_blob.StorageSharedKeyCredential) return blob.generateSasUrl({
@@ -87,7 +94,8 @@ function azureBlobStorage(config) {
87
94
  protocol: PROTOCOL,
88
95
  async listObjects(prefix) {
89
96
  const objects = [];
90
- for await (const blob of container().listBlobsFlat({ prefix: getListPrefix(prefix) })) {
97
+ const blobs = await container();
98
+ for await (const blob of blobs.listBlobsFlat({ prefix: getListPrefix(prefix) })) {
91
99
  const storageUri = getListedStorageUri(blob.name);
92
100
  if (storageUri === null) continue;
93
101
  objects.push({
@@ -104,7 +112,8 @@ function azureBlobStorage(config) {
104
112
  for (let offset = 0; offset < keys.length; offset += 256) {
105
113
  const batch = keys.slice(offset, offset + 256);
106
114
  if (batch.length === 0) continue;
107
- const failed = (await container().getBlobBatchClient().deleteBlobs(batch.map((key) => container().getBlobClient(key)))).subResponses.filter(({ status }) => status >= 300 && status !== 404);
115
+ const blobs = await container();
116
+ const failed = (await blobs.getBlobBatchClient().deleteBlobs(batch.map((key) => blobs.getBlobClient(key)))).subResponses.filter(({ status }) => status >= 300 && status !== 404);
108
117
  if (failed.length > 0) throw new Error(`Failed to delete ${failed.length} Azure blob(s).`);
109
118
  }
110
119
  },
@@ -114,7 +123,7 @@ function azureBlobStorage(config) {
114
123
  blobContentType: contentType,
115
124
  blobCacheControl: "max-age=31536000"
116
125
  } };
117
- const blob = container().getBlockBlobClient(storageKey);
126
+ const blob = (await container()).getBlockBlobClient(storageKey);
118
127
  if (contentLength === void 0) await blob.uploadData(new Uint8Array(await new Response(body).arrayBuffer()), options);
119
128
  else {
120
129
  const { Readable } = await import("node:stream");
@@ -143,7 +152,7 @@ function azureBlobStorage(config) {
143
152
  async get({ storageUri }) {
144
153
  const { key } = parseAndValidate(storageUri);
145
154
  try {
146
- const download = await container().getBlobClient(key).download();
155
+ const download = await (await container()).getBlobClient(key).download();
147
156
  const body = download.readableStreamBody;
148
157
  if (body === void 0) return { response: null };
149
158
  const headers = new Headers();
@@ -163,11 +172,11 @@ function azureBlobStorage(config) {
163
172
  },
164
173
  async exists({ storageUri }) {
165
174
  const { key } = parseAndValidate(storageUri);
166
- return { exists: await container().getBlobClient(key).exists() };
175
+ return { exists: await (await container()).getBlobClient(key).exists() };
167
176
  },
168
177
  async delete({ storageUri }) {
169
178
  const { key } = parseAndValidate(storageUri);
170
- await container().getBlobClient(key).deleteIfExists();
179
+ await (await container()).getBlobClient(key).deleteIfExists();
171
180
  return { deleted: true };
172
181
  }
173
182
  });
@@ -385,14 +394,17 @@ const emulatorAborted = (error) => statusOf(error) === 500 && /PostgresError\(Sq
385
394
  */
386
395
  const createCosmosStore = ({ client, databaseId, containerId, engine, nativePageSize }) => {
387
396
  let opened;
388
- const open = () => opened ??= (() => {
389
- const account = typeof client === "function" ? client() : client;
397
+ const open = () => opened ??= (async () => {
398
+ const account = typeof client === "function" ? await client() : client;
390
399
  return {
391
400
  client: account,
392
401
  container: account.database(databaseId).container(containerId)
393
402
  };
394
- })();
395
- const container = () => open().container;
403
+ })().catch((error) => {
404
+ opened = void 0;
405
+ throw error;
406
+ });
407
+ const container = async () => (await open()).container;
396
408
  /**
397
409
  * The state of the items `ids` names, by id, with the rows of `rowIds`;
398
410
  * a missing item has none.
@@ -400,7 +412,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
400
412
  const readStates = async (ids, rowIds) => {
401
413
  const states = /* @__PURE__ */ new Map();
402
414
  for (const [group, projection] of [[ids.filter((id) => !rowIds.has(id)), ""], [ids.filter((id) => rowIds.has(id)), ", c.row"]]) for (let at = 0; at < group.length; at += GET_BATCH) {
403
- const { resources } = await container().items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
415
+ const { resources } = await (await container()).items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
404
416
  for (const { id, etag, v, expireAt, row } of resources) states.set(id, {
405
417
  etag,
406
418
  v,
@@ -538,7 +550,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
538
550
  const found = /* @__PURE__ */ new Map();
539
551
  const ids = [...new Set(keys.map(cosmosItemId))];
540
552
  for (let at = 0; at < ids.length; at += GET_BATCH) {
541
- const { resources } = await container().items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
553
+ const { resources } = await (await container()).items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
542
554
  for (const resource of resources) found.set(resource.id, toRow(resource.row));
543
555
  }
544
556
  return keys.map((key) => found.get(cosmosItemId(key)) ?? null);
@@ -574,7 +586,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
574
586
  value: after
575
587
  });
576
588
  }
577
- const { resources } = await container().items.query({
589
+ const { resources } = await (await container()).items.query({
578
590
  query: `SELECT TOP @size c.sk, c.row FROM c WHERE ${filters.join(" AND ")} ORDER BY c.pk ${order === "asc" ? "ASC" : "DESC"}, c.sk ${order === "asc" ? "ASC" : "DESC"}`,
579
591
  parameters
580
592
  }, {
@@ -624,7 +636,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
624
636
  if (planned.length > BATCH_OPERATIONS) throw new Error(`A Cosmos DB batch takes ${BATCH_OPERATIONS} operations; this write needs ${planned.length}.`);
625
637
  let results;
626
638
  try {
627
- const response = await container().items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
639
+ const response = await (await container()).items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
628
640
  if (response.result === void 0) throw Object.assign(/* @__PURE__ */ new Error(`Cosmos DB refused the batch with status ${response.code}`), { code: response.code });
629
641
  results = response.result;
630
642
  } catch (error) {
@@ -658,7 +670,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
658
670
  async deleteConsumed(keys) {
659
671
  let remaining = [...new Set(keys.map(cosmosItemId))];
660
672
  for (let attempt = 0; remaining.length > 0; attempt += 1) {
661
- const failed = (await container().items.executeBulkOperations(remaining.map((id) => ({
673
+ const failed = (await (await container()).items.executeBulkOperations(remaining.map((id) => ({
662
674
  operationType: "Delete",
663
675
  id,
664
676
  partitionKey: COSMOS_PARTITION
@@ -679,8 +691,8 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
679
691
  migrations: {
680
692
  /** Creates the database and the container when they are missing. */
681
693
  async apply() {
682
- if (engine !== void 0) await assertCosmosContainerReusable(container(), engine);
683
- const { database } = await open().client.databases.createIfNotExists({ id: databaseId });
694
+ if (engine !== void 0) await assertCosmosContainerReusable(await container(), engine);
695
+ const { database } = await (await open()).client.databases.createIfNotExists({ id: databaseId });
684
696
  const { resource } = await database.containers.createIfNotExists({
685
697
  id: containerId,
686
698
  partitionKey: { paths: [COSMOS_PARTITION_KEY_PATH] },
@@ -717,8 +729,12 @@ const adapterOf = (config) => {
717
729
  const { databaseId = AZURE_COSMOS_V1_DATABASE, containerId = AZURE_COSMOS_V1_CONTAINER, aggregateBatching: _, ...connection } = config;
718
730
  if (connection.consistencyLevel !== void 0 && connection.consistencyLevel !== "Strong") throw new Error(`cosmosDatabase reads with strong consistency; consistencyLevel "${String(connection.consistencyLevel)}" is weaker.`);
719
731
  let client;
732
+ const { connectionString } = connection;
720
733
  const store = createCosmosStore({
721
- client: () => client ??= new _azure_cosmos.CosmosClient(connection),
734
+ client: async () => client ??= new _azure_cosmos.CosmosClient({
735
+ ...connection,
736
+ connectionString: typeof connectionString === "function" ? await connectionString() : connectionString
737
+ }),
722
738
  databaseId,
723
739
  containerId,
724
740
  engine: currentCosmosEngine()
@@ -747,7 +763,7 @@ const migrateCosmosDatabase = (config, plugins = []) => {
747
763
  const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
748
764
  /** The account host a client reaches: its endpoint, or the connection string's. */
749
765
  const accountHostOf = ({ endpoint, connectionString }) => {
750
- const url = endpoint ?? connectionString?.match(/AccountEndpoint=([^;]+)/iu)?.[1];
766
+ const url = endpoint ?? (typeof connectionString === "string" ? connectionString.match(/AccountEndpoint=([^;]+)/iu)?.[1] : void 0);
751
767
  try {
752
768
  return url === void 0 ? void 0 : new URL(url).hostname;
753
769
  } catch {
package/dist/index.d.cts CHANGED
@@ -5,7 +5,12 @@ import { CosmosClientOptions } from "@azure/cosmos";
5
5
  //#region src/azureBlobStorage.d.ts
6
6
  /** The account to connect to: its connection string, or its Blob endpoint and a credential. */
7
7
  type AzureBlobConnection = {
8
- readonly connectionString: string;
8
+ /**
9
+ * The account's connection string, or a function that returns it,
10
+ * such as one that reads it with `secretEnv`: called on first use, so
11
+ * a config loads without it.
12
+ */
13
+ readonly connectionString: string | (() => string | Promise<string>);
9
14
  } | {
10
15
  /** The Blob service endpoint, such as `https://<account>.blob.core.windows.net`. */
11
16
  readonly accountUrl: string;
@@ -38,7 +43,14 @@ declare function azureBlobStorage(config: AzureBlobStorageConfig): StorageAdapte
38
43
  * `endpoint` with a `key` or Microsoft Entra `aadCredentials`. Reads must
39
44
  * be strongly consistent, so `consistencyLevel` takes only `"Strong"`.
40
45
  */
41
- type CosmosDatabaseConfig = Omit<CosmosClientOptions, "consistencyLevel"> & {
46
+ type CosmosDatabaseConfig = Omit<CosmosClientOptions, "consistencyLevel" | "connectionString"> & {
47
+ /**
48
+ * The account's connection string, or a function that returns it, such as
49
+ * one that reads it with `secretEnv`: called on the first request, so a
50
+ * config loads without it. `endpoint` beside a function names the
51
+ * account to `hot-updater doctor`.
52
+ */
53
+ readonly connectionString?: string | (() => string | Promise<string>);
42
54
  readonly consistencyLevel?: "Strong";
43
55
  /** The database the container lives in. @default "hot-updater" */
44
56
  readonly databaseId?: string;
package/dist/index.d.mts CHANGED
@@ -5,7 +5,12 @@ import { CosmosClientOptions } from "@azure/cosmos";
5
5
  //#region src/azureBlobStorage.d.ts
6
6
  /** The account to connect to: its connection string, or its Blob endpoint and a credential. */
7
7
  type AzureBlobConnection = {
8
- readonly connectionString: string;
8
+ /**
9
+ * The account's connection string, or a function that returns it,
10
+ * such as one that reads it with `secretEnv`: called on first use, so
11
+ * a config loads without it.
12
+ */
13
+ readonly connectionString: string | (() => string | Promise<string>);
9
14
  } | {
10
15
  /** The Blob service endpoint, such as `https://<account>.blob.core.windows.net`. */
11
16
  readonly accountUrl: string;
@@ -38,7 +43,14 @@ declare function azureBlobStorage(config: AzureBlobStorageConfig): StorageAdapte
38
43
  * `endpoint` with a `key` or Microsoft Entra `aadCredentials`. Reads must
39
44
  * be strongly consistent, so `consistencyLevel` takes only `"Strong"`.
40
45
  */
41
- type CosmosDatabaseConfig = Omit<CosmosClientOptions, "consistencyLevel"> & {
46
+ type CosmosDatabaseConfig = Omit<CosmosClientOptions, "consistencyLevel" | "connectionString"> & {
47
+ /**
48
+ * The account's connection string, or a function that returns it, such as
49
+ * one that reads it with `secretEnv`: called on the first request, so a
50
+ * config loads without it. `endpoint` beside a function names the
51
+ * account to `hot-updater doctor`.
52
+ */
53
+ readonly connectionString?: string | (() => string | Promise<string>);
42
54
  readonly consistencyLevel?: "Strong";
43
55
  /** The database the container lives in. @default "hot-updater" */
44
56
  readonly databaseId?: string;
package/dist/index.mjs CHANGED
@@ -10,7 +10,11 @@ const SAS_EXPIRES_IN_MS = 36e5;
10
10
  /** A user delegation key signs SAS URLs for up to seven days; it is renewed well before. */
11
11
  const DELEGATION_KEY_LIFETIME_MS = 864e5;
12
12
  const isNotFound = (error) => error instanceof RestError && error.statusCode === 404;
13
- const serviceOf = (config) => "connectionString" in config ? BlobServiceClient.fromConnectionString(config.connectionString) : new BlobServiceClient(config.accountUrl, config.credential);
13
+ const serviceOf = async (config) => {
14
+ if (!("connectionString" in config)) return new BlobServiceClient(config.accountUrl, config.credential);
15
+ const { connectionString } = config;
16
+ return BlobServiceClient.fromConnectionString(typeof connectionString === "function" ? await connectionString() : connectionString);
17
+ };
14
18
  /**
15
19
  * Bundles in an Azure Blob Storage container. Without `getDownloadUrl`,
16
20
  * devices download through SAS URLs: signed with the account key when the
@@ -20,8 +24,11 @@ const serviceOf = (config) => "connectionString" in config ? BlobServiceClient.f
20
24
  function azureBlobStorage(config) {
21
25
  const { containerName, basePath, getDownloadUrl } = config;
22
26
  let client;
23
- const service = () => client ??= serviceOf(config);
24
- const container = () => service().getContainerClient(containerName);
27
+ const service = () => client ??= serviceOf(config).catch((error) => {
28
+ client = void 0;
29
+ throw error;
30
+ });
31
+ const container = async () => (await service()).getContainerClient(containerName);
25
32
  const normalizedBasePath = basePath?.replace(/^\/+|\/+$/g, "") ?? "";
26
33
  const getStorageKey = createStorageKeyBuilder(normalizedBasePath);
27
34
  const getListPrefix = (prefix = "") => {
@@ -53,7 +60,7 @@ function azureBlobStorage(config) {
53
60
  const delegationKey = () => {
54
61
  const now = Date.now();
55
62
  if (delegation === void 0 || delegation.renewAt <= now) {
56
- const key = service().getUserDelegationKey(/* @__PURE__ */ new Date(now - 3e5), new Date(now + DELEGATION_KEY_LIFETIME_MS));
63
+ const key = service().then((blobService) => blobService.getUserDelegationKey(/* @__PURE__ */ new Date(now - 3e5), new Date(now + DELEGATION_KEY_LIFETIME_MS)));
57
64
  key.catch(() => {
58
65
  delegation = void 0;
59
66
  });
@@ -65,7 +72,7 @@ function azureBlobStorage(config) {
65
72
  return delegation.key;
66
73
  };
67
74
  const signedUrl = async (key) => {
68
- const blob = container().getBlobClient(key);
75
+ const blob = (await container()).getBlobClient(key);
69
76
  const expiresOn = new Date(Date.now() + SAS_EXPIRES_IN_MS);
70
77
  const permissions = BlobSASPermissions.parse("r");
71
78
  if (blob.credential instanceof StorageSharedKeyCredential) return blob.generateSasUrl({
@@ -86,7 +93,8 @@ function azureBlobStorage(config) {
86
93
  protocol: PROTOCOL,
87
94
  async listObjects(prefix) {
88
95
  const objects = [];
89
- for await (const blob of container().listBlobsFlat({ prefix: getListPrefix(prefix) })) {
96
+ const blobs = await container();
97
+ for await (const blob of blobs.listBlobsFlat({ prefix: getListPrefix(prefix) })) {
90
98
  const storageUri = getListedStorageUri(blob.name);
91
99
  if (storageUri === null) continue;
92
100
  objects.push({
@@ -103,7 +111,8 @@ function azureBlobStorage(config) {
103
111
  for (let offset = 0; offset < keys.length; offset += 256) {
104
112
  const batch = keys.slice(offset, offset + 256);
105
113
  if (batch.length === 0) continue;
106
- const failed = (await container().getBlobBatchClient().deleteBlobs(batch.map((key) => container().getBlobClient(key)))).subResponses.filter(({ status }) => status >= 300 && status !== 404);
114
+ const blobs = await container();
115
+ const failed = (await blobs.getBlobBatchClient().deleteBlobs(batch.map((key) => blobs.getBlobClient(key)))).subResponses.filter(({ status }) => status >= 300 && status !== 404);
107
116
  if (failed.length > 0) throw new Error(`Failed to delete ${failed.length} Azure blob(s).`);
108
117
  }
109
118
  },
@@ -113,7 +122,7 @@ function azureBlobStorage(config) {
113
122
  blobContentType: contentType,
114
123
  blobCacheControl: "max-age=31536000"
115
124
  } };
116
- const blob = container().getBlockBlobClient(storageKey);
125
+ const blob = (await container()).getBlockBlobClient(storageKey);
117
126
  if (contentLength === void 0) await blob.uploadData(new Uint8Array(await new Response(body).arrayBuffer()), options);
118
127
  else {
119
128
  const { Readable } = await import("node:stream");
@@ -142,7 +151,7 @@ function azureBlobStorage(config) {
142
151
  async get({ storageUri }) {
143
152
  const { key } = parseAndValidate(storageUri);
144
153
  try {
145
- const download = await container().getBlobClient(key).download();
154
+ const download = await (await container()).getBlobClient(key).download();
146
155
  const body = download.readableStreamBody;
147
156
  if (body === void 0) return { response: null };
148
157
  const headers = new Headers();
@@ -162,11 +171,11 @@ function azureBlobStorage(config) {
162
171
  },
163
172
  async exists({ storageUri }) {
164
173
  const { key } = parseAndValidate(storageUri);
165
- return { exists: await container().getBlobClient(key).exists() };
174
+ return { exists: await (await container()).getBlobClient(key).exists() };
166
175
  },
167
176
  async delete({ storageUri }) {
168
177
  const { key } = parseAndValidate(storageUri);
169
- await container().getBlobClient(key).deleteIfExists();
178
+ await (await container()).getBlobClient(key).deleteIfExists();
170
179
  return { deleted: true };
171
180
  }
172
181
  });
@@ -384,14 +393,17 @@ const emulatorAborted = (error) => statusOf(error) === 500 && /PostgresError\(Sq
384
393
  */
385
394
  const createCosmosStore = ({ client, databaseId, containerId, engine, nativePageSize }) => {
386
395
  let opened;
387
- const open = () => opened ??= (() => {
388
- const account = typeof client === "function" ? client() : client;
396
+ const open = () => opened ??= (async () => {
397
+ const account = typeof client === "function" ? await client() : client;
389
398
  return {
390
399
  client: account,
391
400
  container: account.database(databaseId).container(containerId)
392
401
  };
393
- })();
394
- const container = () => open().container;
402
+ })().catch((error) => {
403
+ opened = void 0;
404
+ throw error;
405
+ });
406
+ const container = async () => (await open()).container;
395
407
  /**
396
408
  * The state of the items `ids` names, by id, with the rows of `rowIds`;
397
409
  * a missing item has none.
@@ -399,7 +411,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
399
411
  const readStates = async (ids, rowIds) => {
400
412
  const states = /* @__PURE__ */ new Map();
401
413
  for (const [group, projection] of [[ids.filter((id) => !rowIds.has(id)), ""], [ids.filter((id) => rowIds.has(id)), ", c.row"]]) for (let at = 0; at < group.length; at += GET_BATCH) {
402
- const { resources } = await container().items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
414
+ const { resources } = await (await container()).items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
403
415
  for (const { id, etag, v, expireAt, row } of resources) states.set(id, {
404
416
  etag,
405
417
  v,
@@ -537,7 +549,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
537
549
  const found = /* @__PURE__ */ new Map();
538
550
  const ids = [...new Set(keys.map(cosmosItemId))];
539
551
  for (let at = 0; at < ids.length; at += GET_BATCH) {
540
- const { resources } = await container().items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
552
+ const { resources } = await (await container()).items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
541
553
  for (const resource of resources) found.set(resource.id, toRow(resource.row));
542
554
  }
543
555
  return keys.map((key) => found.get(cosmosItemId(key)) ?? null);
@@ -573,7 +585,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
573
585
  value: after
574
586
  });
575
587
  }
576
- const { resources } = await container().items.query({
588
+ const { resources } = await (await container()).items.query({
577
589
  query: `SELECT TOP @size c.sk, c.row FROM c WHERE ${filters.join(" AND ")} ORDER BY c.pk ${order === "asc" ? "ASC" : "DESC"}, c.sk ${order === "asc" ? "ASC" : "DESC"}`,
578
590
  parameters
579
591
  }, {
@@ -623,7 +635,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
623
635
  if (planned.length > BATCH_OPERATIONS) throw new Error(`A Cosmos DB batch takes ${BATCH_OPERATIONS} operations; this write needs ${planned.length}.`);
624
636
  let results;
625
637
  try {
626
- const response = await container().items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
638
+ const response = await (await container()).items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
627
639
  if (response.result === void 0) throw Object.assign(/* @__PURE__ */ new Error(`Cosmos DB refused the batch with status ${response.code}`), { code: response.code });
628
640
  results = response.result;
629
641
  } catch (error) {
@@ -657,7 +669,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
657
669
  async deleteConsumed(keys) {
658
670
  let remaining = [...new Set(keys.map(cosmosItemId))];
659
671
  for (let attempt = 0; remaining.length > 0; attempt += 1) {
660
- const failed = (await container().items.executeBulkOperations(remaining.map((id) => ({
672
+ const failed = (await (await container()).items.executeBulkOperations(remaining.map((id) => ({
661
673
  operationType: "Delete",
662
674
  id,
663
675
  partitionKey: COSMOS_PARTITION
@@ -678,8 +690,8 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
678
690
  migrations: {
679
691
  /** Creates the database and the container when they are missing. */
680
692
  async apply() {
681
- if (engine !== void 0) await assertCosmosContainerReusable(container(), engine);
682
- const { database } = await open().client.databases.createIfNotExists({ id: databaseId });
693
+ if (engine !== void 0) await assertCosmosContainerReusable(await container(), engine);
694
+ const { database } = await (await open()).client.databases.createIfNotExists({ id: databaseId });
683
695
  const { resource } = await database.containers.createIfNotExists({
684
696
  id: containerId,
685
697
  partitionKey: { paths: [COSMOS_PARTITION_KEY_PATH] },
@@ -716,8 +728,12 @@ const adapterOf = (config) => {
716
728
  const { databaseId = AZURE_COSMOS_V1_DATABASE, containerId = AZURE_COSMOS_V1_CONTAINER, aggregateBatching: _, ...connection } = config;
717
729
  if (connection.consistencyLevel !== void 0 && connection.consistencyLevel !== "Strong") throw new Error(`cosmosDatabase reads with strong consistency; consistencyLevel "${String(connection.consistencyLevel)}" is weaker.`);
718
730
  let client;
731
+ const { connectionString } = connection;
719
732
  const store = createCosmosStore({
720
- client: () => client ??= new CosmosClient(connection),
733
+ client: async () => client ??= new CosmosClient({
734
+ ...connection,
735
+ connectionString: typeof connectionString === "function" ? await connectionString() : connectionString
736
+ }),
721
737
  databaseId,
722
738
  containerId,
723
739
  engine: currentCosmosEngine()
@@ -746,7 +762,7 @@ const migrateCosmosDatabase = (config, plugins = []) => {
746
762
  const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
747
763
  /** The account host a client reaches: its endpoint, or the connection string's. */
748
764
  const accountHostOf = ({ endpoint, connectionString }) => {
749
- const url = endpoint ?? connectionString?.match(/AccountEndpoint=([^;]+)/iu)?.[1];
765
+ const url = endpoint ?? (typeof connectionString === "string" ? connectionString.match(/AccountEndpoint=([^;]+)/iu)?.[1] : void 0);
750
766
  try {
751
767
  return url === void 0 ? void 0 : new URL(url).hostname;
752
768
  } catch {
@@ -267,14 +267,17 @@ const emulatorAborted = (error) => statusOf(error) === 500 && /PostgresError\(Sq
267
267
  */
268
268
  const createCosmosStore = ({ client, databaseId, containerId, engine, nativePageSize }) => {
269
269
  let opened;
270
- const open = () => opened ??= (() => {
271
- const account = typeof client === "function" ? client() : client;
270
+ const open = () => opened ??= (async () => {
271
+ const account = typeof client === "function" ? await client() : client;
272
272
  return {
273
273
  client: account,
274
274
  container: account.database(databaseId).container(containerId)
275
275
  };
276
- })();
277
- const container = () => open().container;
276
+ })().catch((error) => {
277
+ opened = void 0;
278
+ throw error;
279
+ });
280
+ const container = async () => (await open()).container;
278
281
  /**
279
282
  * The state of the items `ids` names, by id, with the rows of `rowIds`;
280
283
  * a missing item has none.
@@ -282,7 +285,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
282
285
  const readStates = async (ids, rowIds) => {
283
286
  const states = /* @__PURE__ */ new Map();
284
287
  for (const [group, projection] of [[ids.filter((id) => !rowIds.has(id)), ""], [ids.filter((id) => rowIds.has(id)), ", c.row"]]) for (let at = 0; at < group.length; at += GET_BATCH) {
285
- const { resources } = await container().items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
288
+ const { resources } = await (await container()).items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
286
289
  for (const { id, etag, v, expireAt, row } of resources) states.set(id, {
287
290
  etag,
288
291
  v,
@@ -420,7 +423,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
420
423
  const found = /* @__PURE__ */ new Map();
421
424
  const ids = [...new Set(keys.map(cosmosItemId))];
422
425
  for (let at = 0; at < ids.length; at += GET_BATCH) {
423
- const { resources } = await container().items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
426
+ const { resources } = await (await container()).items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
424
427
  for (const resource of resources) found.set(resource.id, toRow(resource.row));
425
428
  }
426
429
  return keys.map((key) => found.get(cosmosItemId(key)) ?? null);
@@ -456,7 +459,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
456
459
  value: after
457
460
  });
458
461
  }
459
- const { resources } = await container().items.query({
462
+ const { resources } = await (await container()).items.query({
460
463
  query: `SELECT TOP @size c.sk, c.row FROM c WHERE ${filters.join(" AND ")} ORDER BY c.pk ${order === "asc" ? "ASC" : "DESC"}, c.sk ${order === "asc" ? "ASC" : "DESC"}`,
461
464
  parameters
462
465
  }, {
@@ -506,7 +509,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
506
509
  if (planned.length > BATCH_OPERATIONS) throw new Error(`A Cosmos DB batch takes ${BATCH_OPERATIONS} operations; this write needs ${planned.length}.`);
507
510
  let results;
508
511
  try {
509
- const response = await container().items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
512
+ const response = await (await container()).items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
510
513
  if (response.result === void 0) throw Object.assign(/* @__PURE__ */ new Error(`Cosmos DB refused the batch with status ${response.code}`), { code: response.code });
511
514
  results = response.result;
512
515
  } catch (error) {
@@ -540,7 +543,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
540
543
  async deleteConsumed(keys) {
541
544
  let remaining = [...new Set(keys.map(cosmosItemId))];
542
545
  for (let attempt = 0; remaining.length > 0; attempt += 1) {
543
- const failed = (await container().items.executeBulkOperations(remaining.map((id) => ({
546
+ const failed = (await (await container()).items.executeBulkOperations(remaining.map((id) => ({
544
547
  operationType: "Delete",
545
548
  id,
546
549
  partitionKey: COSMOS_PARTITION
@@ -561,8 +564,8 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
561
564
  migrations: {
562
565
  /** Creates the database and the container when they are missing. */
563
566
  async apply() {
564
- if (engine !== void 0) await assertCosmosContainerReusable(container(), engine);
565
- const { database } = await open().client.databases.createIfNotExists({ id: databaseId });
567
+ if (engine !== void 0) await assertCosmosContainerReusable(await container(), engine);
568
+ const { database } = await (await open()).client.databases.createIfNotExists({ id: databaseId });
566
569
  const { resource } = await database.containers.createIfNotExists({
567
570
  id: containerId,
568
571
  partitionKey: { paths: [COSMOS_PARTITION_KEY_PATH] },
@@ -599,8 +602,12 @@ const adapterOf = (config) => {
599
602
  const { databaseId = AZURE_COSMOS_V1_DATABASE, containerId = AZURE_COSMOS_V1_CONTAINER, aggregateBatching: _, ...connection } = config;
600
603
  if (connection.consistencyLevel !== void 0 && connection.consistencyLevel !== "Strong") throw new Error(`cosmosDatabase reads with strong consistency; consistencyLevel "${String(connection.consistencyLevel)}" is weaker.`);
601
604
  let client;
605
+ const { connectionString } = connection;
602
606
  const store = createCosmosStore({
603
- client: () => client ??= new _azure_cosmos.CosmosClient(connection),
607
+ client: async () => client ??= new _azure_cosmos.CosmosClient({
608
+ ...connection,
609
+ connectionString: typeof connectionString === "function" ? await connectionString() : connectionString
610
+ }),
604
611
  databaseId,
605
612
  containerId,
606
613
  engine: currentCosmosEngine()
@@ -629,7 +636,7 @@ const migrateCosmosDatabase = (config, plugins = []) => {
629
636
  const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
630
637
  /** The account host a client reaches: its endpoint, or the connection string's. */
631
638
  const accountHostOf = ({ endpoint, connectionString }) => {
632
- const url = endpoint ?? connectionString?.match(/AccountEndpoint=([^;]+)/iu)?.[1];
639
+ const url = endpoint ?? (typeof connectionString === "string" ? connectionString.match(/AccountEndpoint=([^;]+)/iu)?.[1] : void 0);
633
640
  try {
634
641
  return url === void 0 ? void 0 : new URL(url).hostname;
635
642
  } catch {
@@ -7451,26 +7458,38 @@ const verifyFunction = async (baseURL, { attempts = 60, delayMs = 5e3 } = {}) =>
7451
7458
  * import without its extension, so it names the container itself.
7452
7459
  */
7453
7460
  const BLOB_CONTAINER = "hot-updater";
7454
- const getConfigScaffold = (build) => {
7461
+ /**
7462
+ * The config init writes. With the `resources` init set up, it names the
7463
+ * Cosmos DB account, and reads both connection strings with `secretEnv`,
7464
+ * from the environment and `.env.hotupdater`, then the config's `secrets`
7465
+ * adapter. Without them, as the agent scaffold writes it, it reads them
7466
+ * from the environment.
7467
+ */
7468
+ const getConfigScaffold = (build, resources) => {
7469
+ const read = (reader, name) => resources === void 0 ? `process.env.${name}!` : `async () =>
7470
+ (await ${reader}()).${name}`;
7455
7471
  const storageConfig = {
7456
7472
  imports: [{
7457
7473
  pkg: "@hot-updater/azure",
7458
7474
  named: ["azureBlobStorage"]
7459
7475
  }],
7460
7476
  configString: `azureBlobStorage({
7461
- connectionString: process.env.HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING!,
7477
+ connectionString: ${read("storageSecrets", "HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING")},
7462
7478
  containerName: "${BLOB_CONTAINER}",
7463
7479
  })`
7464
7480
  };
7465
- return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(new _hot_updater_cli_tools.ConfigBuilder().setBuildType(build).setStorage(storageConfig).setDatabase({
7481
+ const databaseConfig = {
7466
7482
  imports: [{
7467
7483
  pkg: "@hot-updater/azure",
7468
7484
  named: ["cosmosDatabase"]
7469
7485
  }],
7470
- configString: `cosmosDatabase({
7471
- connectionString: process.env.HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING!,
7486
+ configString: `cosmosDatabase({${resources?.cosmosEndpoint === void 0 ? "" : `
7487
+ endpoint: ${JSON.stringify(resources.cosmosEndpoint)},`}
7488
+ connectionString: ${read("databaseSecrets", "HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING")},
7472
7489
  })`
7473
- }).setPlugins({
7490
+ };
7491
+ const helperStatements = resources === void 0 ? [] : [(0, _hot_updater_cli_tools.secretEnvHelper)("storageSecrets", ["HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING"]), (0, _hot_updater_cli_tools.secretEnvHelper)("databaseSecrets", ["HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING"])];
7492
+ const builder = new _hot_updater_cli_tools.ConfigBuilder().setBuildType(build).setStorage(storageConfig).setDatabase(databaseConfig).setPlugins({
7474
7493
  imports: [{
7475
7494
  pkg: "hot-updater/plugins",
7476
7495
  named: [
@@ -7480,7 +7499,12 @@ const getConfigScaffold = (build) => {
7480
7499
  ]
7481
7500
  }],
7482
7501
  configString: "[apiKeys(), insights(), remoteConfig()]"
7483
- }));
7502
+ });
7503
+ if (resources !== void 0) builder.addImport({
7504
+ pkg: "hot-updater",
7505
+ named: ["secretEnv"]
7506
+ }).setIntermediateCode(helperStatements.map(({ code }) => code).join("\n\n"));
7507
+ return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(builder, { helperStatements });
7484
7508
  };
7485
7509
  //#endregion
7486
7510
  //#region iac/init/index.ts
@@ -7565,7 +7589,8 @@ const initProvider = {
7565
7589
  optional: true,
7566
7590
  preflight: false
7567
7591
  }
7568
- }
7592
+ },
7593
+ secrets: ["HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING", "HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING"]
7569
7594
  };
7570
7595
  //#endregion
7571
7596
  //#region iac/index.ts
@@ -7625,10 +7650,10 @@ const resolveName = async ({ input, saved, fallback, nonInteractive, validate, h
7625
7650
  validate: (value) => validate(value) ? void 0 : hint
7626
7651
  }));
7627
7652
  };
7628
- const runInit = async ({ build, envFile }) => {
7653
+ const runInit = async ({ build, envFile, secretsEnv }) => {
7629
7654
  const cwd = (0, _hot_updater_cli_tools.getCwd)();
7630
7655
  const nonInteractive = envFile !== void 0;
7631
- const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(cwd, envFile);
7656
+ const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(cwd, envFile, secretsEnv);
7632
7657
  const initInputEnv = (0, _hot_updater_cli_tools.getHotUpdaterInitInputEnv)(initEnvSources, nonInteractive);
7633
7658
  const saved = Object.fromEntries(Object.entries(inputs).map(([name, input]) => [name, initInputEnv[input.envKey]?.trim() || void 0]));
7634
7659
  const cliVersion = await azureCliVersion();
@@ -7835,7 +7860,8 @@ const runInit = async ({ build, envFile }) => {
7835
7860
  await database.dispose?.();
7836
7861
  }
7837
7862
  _hot_updater_cli_tools.p.log.success("Generated '.env.hotupdater' file with Azure settings.");
7838
- await (0, _hot_updater_cli_tools.writeHotUpdaterFiles)(getConfigScaffold(build), {
7863
+ const cosmosEndpoint = /AccountEndpoint=([^;]+)/iu.exec(cosmosConnectionString)?.[1];
7864
+ await (0, _hot_updater_cli_tools.writeHotUpdaterFiles)(getConfigScaffold(build, cosmosEndpoint === void 0 ? {} : { cosmosEndpoint }), {
7839
7865
  cwd,
7840
7866
  settings: "Azure"
7841
7867
  });
@@ -81,6 +81,7 @@ declare const initProvider: {
81
81
  readonly preflight: false;
82
82
  };
83
83
  };
84
+ readonly secrets: readonly ["HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING", "HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING"];
84
85
  };
85
86
  //#endregion
86
87
  //#region iac/index.d.ts
@@ -94,6 +95,6 @@ declare const defaultResourceNames: (subscriptionId: string, resourceGroup: stri
94
95
  cosmosAccount: string;
95
96
  functionApp: string;
96
97
  };
97
- declare const runInit: ({ build, envFile }: RunInitOptions) => Promise<void>;
98
+ declare const runInit: ({ build, envFile, secretsEnv }: RunInitOptions) => Promise<void>;
98
99
  //#endregion
99
100
  export { AZURE_FUNCTION_PACKAGE, defaultResourceNames, initProvider, runInit };
@@ -81,6 +81,7 @@ declare const initProvider: {
81
81
  readonly preflight: false;
82
82
  };
83
83
  };
84
+ readonly secrets: readonly ["HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING", "HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING"];
84
85
  };
85
86
  //#endregion
86
87
  //#region iac/index.d.ts
@@ -94,6 +95,6 @@ declare const defaultResourceNames: (subscriptionId: string, resourceGroup: stri
94
95
  cosmosAccount: string;
95
96
  functionApp: string;
96
97
  };
97
- declare const runInit: ({ build, envFile }: RunInitOptions) => Promise<void>;
98
+ declare const runInit: ({ build, envFile, secretsEnv }: RunInitOptions) => Promise<void>;
98
99
  //#endregion
99
100
  export { AZURE_FUNCTION_PACKAGE, defaultResourceNames, initProvider, runInit };
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { createHash } from "crypto";
3
3
  import path from "path";
4
- import { ConfigBuilder, InitError, assembleServer, createHotUpdaterConfigScaffoldFromBuilder, createZip, getCwd, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, printAppSetup, provisionClientCredential, readHotUpdaterInitEnv, shouldAutoSelectOnlyInitResource, writeHotUpdaterFiles } from "@hot-updater/cli-tools";
4
+ import { ConfigBuilder, InitError, assembleServer, createHotUpdaterConfigScaffoldFromBuilder, createZip, getCwd, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, printAppSetup, provisionClientCredential, readHotUpdaterInitEnv, secretEnvHelper, shouldAutoSelectOnlyInitResource, writeHotUpdaterFiles } from "@hot-updater/cli-tools";
5
5
  import { apiKeys, insights, remoteConfig } from "@hot-updater/server/plugins";
6
6
  import { CosmosClient } from "@azure/cosmos";
7
7
  import { SETTINGS_TABLE, createEngineDatabase, createKvAdapter, encodeKvKey, migrateCoreSchema, toolingTargetOf } from "@hot-updater/plugin-core";
@@ -262,14 +262,17 @@ const emulatorAborted = (error) => statusOf(error) === 500 && /PostgresError\(Sq
262
262
  */
263
263
  const createCosmosStore = ({ client, databaseId, containerId, engine, nativePageSize }) => {
264
264
  let opened;
265
- const open = () => opened ??= (() => {
266
- const account = typeof client === "function" ? client() : client;
265
+ const open = () => opened ??= (async () => {
266
+ const account = typeof client === "function" ? await client() : client;
267
267
  return {
268
268
  client: account,
269
269
  container: account.database(databaseId).container(containerId)
270
270
  };
271
- })();
272
- const container = () => open().container;
271
+ })().catch((error) => {
272
+ opened = void 0;
273
+ throw error;
274
+ });
275
+ const container = async () => (await open()).container;
273
276
  /**
274
277
  * The state of the items `ids` names, by id, with the rows of `rowIds`;
275
278
  * a missing item has none.
@@ -277,7 +280,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
277
280
  const readStates = async (ids, rowIds) => {
278
281
  const states = /* @__PURE__ */ new Map();
279
282
  for (const [group, projection] of [[ids.filter((id) => !rowIds.has(id)), ""], [ids.filter((id) => rowIds.has(id)), ", c.row"]]) for (let at = 0; at < group.length; at += GET_BATCH) {
280
- const { resources } = await container().items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
283
+ const { resources } = await (await container()).items.query(`SELECT c.id, c._etag AS etag, c.row._v AS v, c.expireAt${projection} FROM c WHERE c.id IN (${idList(group.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
281
284
  for (const { id, etag, v, expireAt, row } of resources) states.set(id, {
282
285
  etag,
283
286
  v,
@@ -415,7 +418,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
415
418
  const found = /* @__PURE__ */ new Map();
416
419
  const ids = [...new Set(keys.map(cosmosItemId))];
417
420
  for (let at = 0; at < ids.length; at += GET_BATCH) {
418
- const { resources } = await container().items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
421
+ const { resources } = await (await container()).items.query(`SELECT c.id, c.row FROM c WHERE c.id IN (${idList(ids.slice(at, at + GET_BATCH))})`, { partitionKey: COSMOS_PARTITION }).fetchAll().catch(missingContainer);
419
422
  for (const resource of resources) found.set(resource.id, toRow(resource.row));
420
423
  }
421
424
  return keys.map((key) => found.get(cosmosItemId(key)) ?? null);
@@ -451,7 +454,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
451
454
  value: after
452
455
  });
453
456
  }
454
- const { resources } = await container().items.query({
457
+ const { resources } = await (await container()).items.query({
455
458
  query: `SELECT TOP @size c.sk, c.row FROM c WHERE ${filters.join(" AND ")} ORDER BY c.pk ${order === "asc" ? "ASC" : "DESC"}, c.sk ${order === "asc" ? "ASC" : "DESC"}`,
456
459
  parameters
457
460
  }, {
@@ -501,7 +504,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
501
504
  if (planned.length > BATCH_OPERATIONS) throw new Error(`A Cosmos DB batch takes ${BATCH_OPERATIONS} operations; this write needs ${planned.length}.`);
502
505
  let results;
503
506
  try {
504
- const response = await container().items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
507
+ const response = await (await container()).items.batch(planned.map(({ operation }) => operation), COSMOS_PARTITION);
505
508
  if (response.result === void 0) throw Object.assign(/* @__PURE__ */ new Error(`Cosmos DB refused the batch with status ${response.code}`), { code: response.code });
506
509
  results = response.result;
507
510
  } catch (error) {
@@ -535,7 +538,7 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
535
538
  async deleteConsumed(keys) {
536
539
  let remaining = [...new Set(keys.map(cosmosItemId))];
537
540
  for (let attempt = 0; remaining.length > 0; attempt += 1) {
538
- const failed = (await container().items.executeBulkOperations(remaining.map((id) => ({
541
+ const failed = (await (await container()).items.executeBulkOperations(remaining.map((id) => ({
539
542
  operationType: "Delete",
540
543
  id,
541
544
  partitionKey: COSMOS_PARTITION
@@ -556,8 +559,8 @@ const createCosmosStore = ({ client, databaseId, containerId, engine, nativePage
556
559
  migrations: {
557
560
  /** Creates the database and the container when they are missing. */
558
561
  async apply() {
559
- if (engine !== void 0) await assertCosmosContainerReusable(container(), engine);
560
- const { database } = await open().client.databases.createIfNotExists({ id: databaseId });
562
+ if (engine !== void 0) await assertCosmosContainerReusable(await container(), engine);
563
+ const { database } = await (await open()).client.databases.createIfNotExists({ id: databaseId });
561
564
  const { resource } = await database.containers.createIfNotExists({
562
565
  id: containerId,
563
566
  partitionKey: { paths: [COSMOS_PARTITION_KEY_PATH] },
@@ -594,8 +597,12 @@ const adapterOf = (config) => {
594
597
  const { databaseId = AZURE_COSMOS_V1_DATABASE, containerId = AZURE_COSMOS_V1_CONTAINER, aggregateBatching: _, ...connection } = config;
595
598
  if (connection.consistencyLevel !== void 0 && connection.consistencyLevel !== "Strong") throw new Error(`cosmosDatabase reads with strong consistency; consistencyLevel "${String(connection.consistencyLevel)}" is weaker.`);
596
599
  let client;
600
+ const { connectionString } = connection;
597
601
  const store = createCosmosStore({
598
- client: () => client ??= new CosmosClient(connection),
602
+ client: async () => client ??= new CosmosClient({
603
+ ...connection,
604
+ connectionString: typeof connectionString === "function" ? await connectionString() : connectionString
605
+ }),
599
606
  databaseId,
600
607
  containerId,
601
608
  engine: currentCosmosEngine()
@@ -624,7 +631,7 @@ const migrateCosmosDatabase = (config, plugins = []) => {
624
631
  const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
625
632
  /** The account host a client reaches: its endpoint, or the connection string's. */
626
633
  const accountHostOf = ({ endpoint, connectionString }) => {
627
- const url = endpoint ?? connectionString?.match(/AccountEndpoint=([^;]+)/iu)?.[1];
634
+ const url = endpoint ?? (typeof connectionString === "string" ? connectionString.match(/AccountEndpoint=([^;]+)/iu)?.[1] : void 0);
628
635
  try {
629
636
  return url === void 0 ? void 0 : new URL(url).hostname;
630
637
  } catch {
@@ -7446,26 +7453,38 @@ const verifyFunction = async (baseURL, { attempts = 60, delayMs = 5e3 } = {}) =>
7446
7453
  * import without its extension, so it names the container itself.
7447
7454
  */
7448
7455
  const BLOB_CONTAINER = "hot-updater";
7449
- const getConfigScaffold = (build) => {
7456
+ /**
7457
+ * The config init writes. With the `resources` init set up, it names the
7458
+ * Cosmos DB account, and reads both connection strings with `secretEnv`,
7459
+ * from the environment and `.env.hotupdater`, then the config's `secrets`
7460
+ * adapter. Without them, as the agent scaffold writes it, it reads them
7461
+ * from the environment.
7462
+ */
7463
+ const getConfigScaffold = (build, resources) => {
7464
+ const read = (reader, name) => resources === void 0 ? `process.env.${name}!` : `async () =>
7465
+ (await ${reader}()).${name}`;
7450
7466
  const storageConfig = {
7451
7467
  imports: [{
7452
7468
  pkg: "@hot-updater/azure",
7453
7469
  named: ["azureBlobStorage"]
7454
7470
  }],
7455
7471
  configString: `azureBlobStorage({
7456
- connectionString: process.env.HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING!,
7472
+ connectionString: ${read("storageSecrets", "HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING")},
7457
7473
  containerName: "${BLOB_CONTAINER}",
7458
7474
  })`
7459
7475
  };
7460
- return createHotUpdaterConfigScaffoldFromBuilder(new ConfigBuilder().setBuildType(build).setStorage(storageConfig).setDatabase({
7476
+ const databaseConfig = {
7461
7477
  imports: [{
7462
7478
  pkg: "@hot-updater/azure",
7463
7479
  named: ["cosmosDatabase"]
7464
7480
  }],
7465
- configString: `cosmosDatabase({
7466
- connectionString: process.env.HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING!,
7481
+ configString: `cosmosDatabase({${resources?.cosmosEndpoint === void 0 ? "" : `
7482
+ endpoint: ${JSON.stringify(resources.cosmosEndpoint)},`}
7483
+ connectionString: ${read("databaseSecrets", "HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING")},
7467
7484
  })`
7468
- }).setPlugins({
7485
+ };
7486
+ const helperStatements = resources === void 0 ? [] : [secretEnvHelper("storageSecrets", ["HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING"]), secretEnvHelper("databaseSecrets", ["HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING"])];
7487
+ const builder = new ConfigBuilder().setBuildType(build).setStorage(storageConfig).setDatabase(databaseConfig).setPlugins({
7469
7488
  imports: [{
7470
7489
  pkg: "hot-updater/plugins",
7471
7490
  named: [
@@ -7475,7 +7494,12 @@ const getConfigScaffold = (build) => {
7475
7494
  ]
7476
7495
  }],
7477
7496
  configString: "[apiKeys(), insights(), remoteConfig()]"
7478
- }));
7497
+ });
7498
+ if (resources !== void 0) builder.addImport({
7499
+ pkg: "hot-updater",
7500
+ named: ["secretEnv"]
7501
+ }).setIntermediateCode(helperStatements.map(({ code }) => code).join("\n\n"));
7502
+ return createHotUpdaterConfigScaffoldFromBuilder(builder, { helperStatements });
7479
7503
  };
7480
7504
  //#endregion
7481
7505
  //#region iac/init/index.ts
@@ -7560,7 +7584,8 @@ const initProvider = {
7560
7584
  optional: true,
7561
7585
  preflight: false
7562
7586
  }
7563
- }
7587
+ },
7588
+ secrets: ["HOT_UPDATER_AZURE_STORAGE_CONNECTION_STRING", "HOT_UPDATER_AZURE_COSMOS_CONNECTION_STRING"]
7564
7589
  };
7565
7590
  //#endregion
7566
7591
  //#region iac/index.ts
@@ -7620,10 +7645,10 @@ const resolveName = async ({ input, saved, fallback, nonInteractive, validate, h
7620
7645
  validate: (value) => validate(value) ? void 0 : hint
7621
7646
  }));
7622
7647
  };
7623
- const runInit = async ({ build, envFile }) => {
7648
+ const runInit = async ({ build, envFile, secretsEnv }) => {
7624
7649
  const cwd = getCwd();
7625
7650
  const nonInteractive = envFile !== void 0;
7626
- const initEnvSources = await readHotUpdaterInitEnv(cwd, envFile);
7651
+ const initEnvSources = await readHotUpdaterInitEnv(cwd, envFile, secretsEnv);
7627
7652
  const initInputEnv = getHotUpdaterInitInputEnv(initEnvSources, nonInteractive);
7628
7653
  const saved = Object.fromEntries(Object.entries(inputs).map(([name, input]) => [name, initInputEnv[input.envKey]?.trim() || void 0]));
7629
7654
  const cliVersion = await azureCliVersion();
@@ -7830,7 +7855,8 @@ const runInit = async ({ build, envFile }) => {
7830
7855
  await database.dispose?.();
7831
7856
  }
7832
7857
  p.log.success("Generated '.env.hotupdater' file with Azure settings.");
7833
- await writeHotUpdaterFiles(getConfigScaffold(build), {
7858
+ const cosmosEndpoint = /AccountEndpoint=([^;]+)/iu.exec(cosmosConnectionString)?.[1];
7859
+ await writeHotUpdaterFiles(getConfigScaffold(build, cosmosEndpoint === void 0 ? {} : { cosmosEndpoint }), {
7834
7860
  cwd,
7835
7861
  settings: "Azure"
7836
7862
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/azure",
3
3
  "type": "module",
4
- "version": "1.2.0-rc.4",
4
+ "version": "1.2.0-rc.5",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "engines": {
7
7
  "node": "^22.23.4 || ^24.22.1 || >=26.12.1"
@@ -42,10 +42,10 @@
42
42
  "@azure/core-auth": "1.11.0",
43
43
  "@azure/cosmos": "4.10.1",
44
44
  "@azure/storage-blob": "12.34.0",
45
- "@hot-updater/plugin-core": "1.2.0-rc.4",
46
- "@hot-updater/cli-tools": "1.2.0-rc.4",
47
- "@hot-updater/server": "1.2.0-rc.4",
48
- "@hot-updater/protocol": "1.2.0-rc.4"
45
+ "@hot-updater/cli-tools": "1.2.0-rc.5",
46
+ "@hot-updater/plugin-core": "1.2.0-rc.5",
47
+ "@hot-updater/protocol": "1.2.0-rc.5",
48
+ "@hot-updater/server": "1.2.0-rc.5"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@azure/functions": "4.16.5",
@@ -53,7 +53,7 @@
53
53
  "execa": "9.5.2",
54
54
  "jszip": "^3.10.1",
55
55
  "vitest": "4.1.4",
56
- "@hot-updater/test-utils": "1.2.0-rc.4"
56
+ "@hot-updater/test-utils": "1.2.0-rc.5"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "tsdown",