@absolutejs/deploy 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/appStoreConnect.js +11 -9
- package/dist/appStoreConnect.js.map +2 -2
- package/dist/cloudTarget.d.ts +4 -4
- package/dist/cloudflare.d.ts +2 -2
- package/dist/cloudflare.js +11 -9
- package/dist/cloudflare.js.map +3 -3
- package/dist/deployer.d.ts +9 -9
- package/dist/digitalocean.d.ts +5 -5
- package/dist/digitalocean.js +21 -13
- package/dist/digitalocean.js.map +5 -5
- package/dist/digitaloceanDns.d.ts +2 -2
- package/dist/digitaloceanDns.js +16 -8
- package/dist/digitaloceanDns.js.map +6 -6
- package/dist/digitaloceanEphemeralInfrastructure.js +16 -8
- package/dist/digitaloceanEphemeralInfrastructure.js.map +5 -5
- package/dist/digitaloceanInfrastructure.js +16 -8
- package/dist/digitaloceanInfrastructure.js.map +6 -6
- package/dist/digitaloceanIngress.js +16 -8
- package/dist/digitaloceanIngress.js.map +5 -5
- package/dist/dns.d.ts +1 -1
- package/dist/dns.js +9 -7
- package/dist/dns.js.map +3 -3
- package/dist/edgeIngress.js +11 -9
- package/dist/edgeIngress.js.map +2 -2
- package/dist/env.d.ts +1 -1
- package/dist/env.js +13 -11
- package/dist/env.js.map +3 -3
- package/dist/gcp.js +99 -93
- package/dist/gcp.js.map +3 -3
- package/dist/gcpIngress.js +93 -91
- package/dist/gcpIngress.js.map +2 -2
- package/dist/googlePlay.js +93 -91
- package/dist/googlePlay.js.map +2 -2
- package/dist/hetzner.d.ts +3 -3
- package/dist/hetzner.js +21 -13
- package/dist/hetzner.js.map +5 -5
- package/dist/hetznerDns.d.ts +2 -2
- package/dist/hetznerDns.js +11 -9
- package/dist/hetznerDns.js.map +3 -3
- package/dist/hetznerInfrastructure.js +16 -8
- package/dist/hetznerInfrastructure.js.map +7 -7
- package/dist/index.d.ts +2 -0
- package/dist/index.js +759 -285
- package/dist/index.js.map +7 -6
- package/dist/linode.d.ts +3 -3
- package/dist/linode.js +21 -13
- package/dist/linode.js.map +5 -5
- package/dist/linodeInfrastructure.js +21 -9
- package/dist/linodeInfrastructure.js.map +7 -7
- package/dist/managedPreview.js +9 -7
- package/dist/managedPreview.js.map +2 -2
- package/dist/mobileUpdate.d.ts +109 -0
- package/dist/mobileUpdate.js +464 -0
- package/dist/mobileUpdate.js.map +10 -0
- package/dist/nativeRelease.js +11 -9
- package/dist/nativeRelease.js.map +2 -2
- package/dist/preview.d.ts +3 -3
- package/dist/preview.js +15 -10
- package/dist/preview.js.map +3 -3
- package/dist/processManagers.d.ts +4 -4
- package/dist/releaseArtifact.js +12 -10
- package/dist/releaseArtifact.js.map +2 -2
- package/dist/route53.d.ts +2 -2
- package/dist/route53.js +9 -7
- package/dist/route53.js.map +3 -3
- package/dist/targets.d.ts +1 -1
- package/dist/tls.d.ts +5 -5
- package/dist/tls.js +23 -18
- package/dist/tls.js.map +3 -3
- package/dist/vultr.d.ts +4 -4
- package/dist/vultr.js +21 -13
- package/dist/vultr.js.map +5 -5
- package/dist/vultrInfrastructure.js +16 -8
- package/dist/vultrInfrastructure.js.map +7 -7
- package/package.json +7 -2
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(mod))
|
|
24
|
+
if (!__hasOwnProp.call(to, key))
|
|
25
|
+
__defProp(to, key, {
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
27
|
+
enumerable: true
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (canCache)
|
|
31
|
+
cache.set(mod, to);
|
|
32
|
+
return to;
|
|
33
|
+
};
|
|
34
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
35
|
+
var __require = import.meta.require;
|
|
36
|
+
|
|
37
|
+
// src/mobileUpdate.ts
|
|
38
|
+
import { createHash, createPublicKey, verify } from "crypto";
|
|
39
|
+
import { readFile, stat } from "fs/promises";
|
|
40
|
+
import path from "path";
|
|
41
|
+
var MOBILE_UPDATE_REGISTRY_FORMAT = 1;
|
|
42
|
+
var DEFAULT_PREFIX = "absolutejs/mobile-updates";
|
|
43
|
+
var MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
44
|
+
var MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
45
|
+
var HASH = /^[a-f0-9]{64}$/;
|
|
46
|
+
var RELEASE = /^amu_[a-f0-9]{64}$/;
|
|
47
|
+
var APP_ID = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
48
|
+
var NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
49
|
+
|
|
50
|
+
class MobileUpdateRegistryError extends Error {
|
|
51
|
+
}
|
|
52
|
+
var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
53
|
+
var text = (value, field) => {
|
|
54
|
+
if (typeof value !== "string" || value.length === 0)
|
|
55
|
+
throw new MobileUpdateRegistryError(`Mobile update ${field} is invalid`);
|
|
56
|
+
return value;
|
|
57
|
+
};
|
|
58
|
+
var iso = (value) => typeof value === "string" && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
|
|
59
|
+
var safePath = (value) => {
|
|
60
|
+
const file = text(value, "file path").replaceAll("\\", "/");
|
|
61
|
+
if (file.startsWith("/") || file.split("/").some((segment) => !segment || segment === "." || segment === ".."))
|
|
62
|
+
throw new MobileUpdateRegistryError("Mobile update file path is invalid");
|
|
63
|
+
return file;
|
|
64
|
+
};
|
|
65
|
+
var parseMobileUpdateManifest = (value) => {
|
|
66
|
+
if (!object(value) || value.format !== 1)
|
|
67
|
+
throw new MobileUpdateRegistryError("Mobile update manifest is invalid");
|
|
68
|
+
const appId = text(value.appId, "appId");
|
|
69
|
+
const channel = text(value.channel, "channel");
|
|
70
|
+
const releaseId = text(value.releaseId, "releaseId");
|
|
71
|
+
const runtimeFingerprint = text(value.runtimeFingerprint, "runtime");
|
|
72
|
+
if (!APP_ID.test(appId) || !NAME.test(channel) || !RELEASE.test(releaseId))
|
|
73
|
+
throw new MobileUpdateRegistryError("Mobile update identity is invalid");
|
|
74
|
+
if (!HASH.test(runtimeFingerprint) || !iso(value.createdAt))
|
|
75
|
+
throw new MobileUpdateRegistryError("Mobile update runtime or timestamp is invalid");
|
|
76
|
+
if (value.classification !== "bug-fix" && value.classification !== "content" && value.classification !== "security")
|
|
77
|
+
throw new MobileUpdateRegistryError("Mobile update classification is invalid");
|
|
78
|
+
if (value.withinSubmittedPurpose !== true)
|
|
79
|
+
throw new MobileUpdateRegistryError("Mobile update policy attestation is missing");
|
|
80
|
+
if (!Array.isArray(value.files) || value.files.length === 0)
|
|
81
|
+
throw new MobileUpdateRegistryError("Mobile update file inventory is invalid");
|
|
82
|
+
const files = value.files.map((candidate) => {
|
|
83
|
+
if (!object(candidate))
|
|
84
|
+
throw new MobileUpdateRegistryError("Mobile update file is invalid");
|
|
85
|
+
const filePath = safePath(candidate.path);
|
|
86
|
+
if (!Number.isSafeInteger(candidate.bytes) || Number(candidate.bytes) < 0 || Number(candidate.bytes) > MAX_FILE_BYTES || typeof candidate.sha256 !== "string" || !HASH.test(candidate.sha256))
|
|
87
|
+
throw new MobileUpdateRegistryError(`Mobile update file ${filePath} is invalid`);
|
|
88
|
+
return {
|
|
89
|
+
bytes: Number(candidate.bytes),
|
|
90
|
+
path: filePath,
|
|
91
|
+
sha256: candidate.sha256
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
if (files.reduce((total, file) => total + file.bytes, 0) > MAX_TOTAL_BYTES || new Set(files.map((file) => file.path)).size !== files.length || files.some((file, index) => index > 0 ? file.path.localeCompare(files[index - 1]?.path ?? "") <= 0 : false))
|
|
95
|
+
throw new MobileUpdateRegistryError("Mobile update file inventory is invalid");
|
|
96
|
+
const signatureKeyId = object(value.signature) ? text(value.signature.keyId, "signature key") : "";
|
|
97
|
+
const signatureValue = object(value.signature) ? text(value.signature.value, "signature") : "";
|
|
98
|
+
if (!object(value.signature) || value.signature.algorithm !== "ecdsa-p256-sha256" || !NAME.test(signatureKeyId) || !/^[A-Za-z0-9+/]+={0,2}$/.test(signatureValue) || Buffer.from(signatureValue, "base64").byteLength !== 64 || Buffer.from(signatureValue, "base64").toString("base64") !== signatureValue)
|
|
99
|
+
throw new MobileUpdateRegistryError("Mobile update signature is invalid");
|
|
100
|
+
return {
|
|
101
|
+
appId,
|
|
102
|
+
channel,
|
|
103
|
+
classification: value.classification,
|
|
104
|
+
createdAt: value.createdAt,
|
|
105
|
+
files,
|
|
106
|
+
format: 1,
|
|
107
|
+
releaseId,
|
|
108
|
+
runtimeFingerprint,
|
|
109
|
+
signature: {
|
|
110
|
+
algorithm: "ecdsa-p256-sha256",
|
|
111
|
+
keyId: signatureKeyId,
|
|
112
|
+
value: signatureValue
|
|
113
|
+
},
|
|
114
|
+
withinSubmittedPurpose: true
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
var canonicalValue = (value) => {
|
|
118
|
+
if (Array.isArray(value))
|
|
119
|
+
return value.map(canonicalValue);
|
|
120
|
+
if (!object(value))
|
|
121
|
+
return value;
|
|
122
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalValue(value[key])]));
|
|
123
|
+
};
|
|
124
|
+
var verifyManifestSignature = (manifest, publicKeys) => {
|
|
125
|
+
const encoded = publicKeys[manifest.signature.keyId];
|
|
126
|
+
if (!encoded)
|
|
127
|
+
throw new MobileUpdateRegistryError("Mobile update signing key is not trusted");
|
|
128
|
+
let publicKey;
|
|
129
|
+
try {
|
|
130
|
+
const der = Buffer.from(encoded, "base64");
|
|
131
|
+
if (der.toString("base64") !== encoded)
|
|
132
|
+
throw new Error("invalid base64");
|
|
133
|
+
publicKey = createPublicKey({ format: "der", key: der, type: "spki" });
|
|
134
|
+
} catch {
|
|
135
|
+
throw new MobileUpdateRegistryError("Mobile update trusted public key is invalid");
|
|
136
|
+
}
|
|
137
|
+
if (publicKey.asymmetricKeyType !== "ec" || publicKey.asymmetricKeyDetails?.namedCurve !== "prime256v1")
|
|
138
|
+
throw new MobileUpdateRegistryError("Mobile update trusted public key must use ECDSA P-256");
|
|
139
|
+
const { signature: _signature, ...unsigned } = manifest;
|
|
140
|
+
if (!verify("sha256", new TextEncoder().encode(JSON.stringify(canonicalValue(unsigned))), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64")))
|
|
141
|
+
throw new MobileUpdateRegistryError("Mobile update signature verification failed");
|
|
142
|
+
};
|
|
143
|
+
var parseChannel = (value) => {
|
|
144
|
+
if (!object(value) || value.format !== MOBILE_UPDATE_REGISTRY_FORMAT)
|
|
145
|
+
throw new MobileUpdateRegistryError("Mobile update channel is invalid");
|
|
146
|
+
const appId = text(value.appId, "channel appId");
|
|
147
|
+
const channel = text(value.channel, "channel");
|
|
148
|
+
if (!APP_ID.test(appId) || !NAME.test(channel) || !iso(value.promotedAt) || typeof value.rollout !== "number" || value.rollout < 0 || value.rollout > 1 || value.releaseId !== undefined && (typeof value.releaseId !== "string" || !RELEASE.test(value.releaseId)) || value.fallbackReleaseId !== undefined && (typeof value.fallbackReleaseId !== "string" || !RELEASE.test(value.fallbackReleaseId)))
|
|
149
|
+
throw new MobileUpdateRegistryError("Mobile update channel is invalid");
|
|
150
|
+
return {
|
|
151
|
+
appId,
|
|
152
|
+
channel,
|
|
153
|
+
...value.fallbackReleaseId ? { fallbackReleaseId: value.fallbackReleaseId } : {},
|
|
154
|
+
format: MOBILE_UPDATE_REGISTRY_FORMAT,
|
|
155
|
+
promotedAt: value.promotedAt,
|
|
156
|
+
...value.releaseId ? { releaseId: value.releaseId } : {},
|
|
157
|
+
rollout: value.rollout
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
var normalizedPrefix = (value) => {
|
|
161
|
+
const prefix = value.replace(/^\/+|\/+$/g, "");
|
|
162
|
+
if (!prefix || prefix.split("/").some((segment) => segment === "." || segment === ".."))
|
|
163
|
+
throw new MobileUpdateRegistryError("Mobile update prefix is invalid");
|
|
164
|
+
return prefix;
|
|
165
|
+
};
|
|
166
|
+
var appHash = (appId) => createHash("sha256").update(appId).digest("hex");
|
|
167
|
+
var digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
168
|
+
var json = (value) => new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
|
|
169
|
+
`);
|
|
170
|
+
var decode = (value) => JSON.parse(new TextDecoder().decode(value));
|
|
171
|
+
var fileDigest = async (file) => {
|
|
172
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
173
|
+
for await (const chunk of file.stream())
|
|
174
|
+
hasher.update(chunk);
|
|
175
|
+
return hasher.digest("hex");
|
|
176
|
+
};
|
|
177
|
+
var rolloutMember = (input) => {
|
|
178
|
+
if (input.rollout === 0)
|
|
179
|
+
return false;
|
|
180
|
+
if (input.rollout === 1)
|
|
181
|
+
return true;
|
|
182
|
+
const value = createHash("sha256").update(`${input.appId}\x00${input.channel}\x00${input.releaseId}\x00${input.installationId}`).digest().readUInt32BE(0);
|
|
183
|
+
return value / 4294967296 < input.rollout;
|
|
184
|
+
};
|
|
185
|
+
var createMobileUpdateRegistry = (options) => {
|
|
186
|
+
const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);
|
|
187
|
+
const clock = options.clock ?? (() => new Date);
|
|
188
|
+
const root = (appId) => `${prefix}/${appHash(appId)}`;
|
|
189
|
+
const releaseRoot = (manifest) => `${root(manifest.appId)}/releases/${manifest.releaseId}`;
|
|
190
|
+
const manifestKey = (manifest) => `${releaseRoot(manifest)}/update.json`;
|
|
191
|
+
const fileKey = (manifest, file) => `${releaseRoot(manifest)}/files/${file.path}`;
|
|
192
|
+
const channelKey = (appId, channel) => {
|
|
193
|
+
if (!APP_ID.test(appId) || !NAME.test(channel))
|
|
194
|
+
throw new MobileUpdateRegistryError("Mobile update channel identity is invalid");
|
|
195
|
+
return `${root(appId)}/channels/${channel}.json`;
|
|
196
|
+
};
|
|
197
|
+
const readManifest = async (appId, releaseId) => {
|
|
198
|
+
const key = manifestKey({ appId, releaseId });
|
|
199
|
+
const bytes = await options.store.get(key);
|
|
200
|
+
if (!bytes)
|
|
201
|
+
return null;
|
|
202
|
+
const head = await options.store.head(key);
|
|
203
|
+
if (!head || head.size !== bytes.byteLength || head.metadata?.sha256 !== digest(bytes))
|
|
204
|
+
throw new MobileUpdateRegistryError("Stored mobile update manifest integrity failed");
|
|
205
|
+
const manifest = parseMobileUpdateManifest(decode(bytes));
|
|
206
|
+
verifyManifestSignature(manifest, options.publicKeys);
|
|
207
|
+
if (manifest.appId !== appId || manifest.releaseId !== releaseId)
|
|
208
|
+
throw new MobileUpdateRegistryError("Stored mobile update identity changed");
|
|
209
|
+
return { key, manifest };
|
|
210
|
+
};
|
|
211
|
+
const readChannel = async (appId, channel) => {
|
|
212
|
+
const bytes = await options.store.get(channelKey(appId, channel));
|
|
213
|
+
if (!bytes)
|
|
214
|
+
return null;
|
|
215
|
+
const value = parseChannel(decode(bytes));
|
|
216
|
+
if (value.appId !== appId || value.channel !== channel)
|
|
217
|
+
throw new MobileUpdateRegistryError("Stored mobile update channel identity changed");
|
|
218
|
+
return value;
|
|
219
|
+
};
|
|
220
|
+
const writeChannel = async (input, signal) => {
|
|
221
|
+
const value = {
|
|
222
|
+
...input,
|
|
223
|
+
format: MOBILE_UPDATE_REGISTRY_FORMAT,
|
|
224
|
+
promotedAt: clock().toISOString()
|
|
225
|
+
};
|
|
226
|
+
const bytes = json(value);
|
|
227
|
+
await options.store.put(channelKey(value.appId, value.channel), bytes, {
|
|
228
|
+
cacheControl: "no-cache",
|
|
229
|
+
contentType: "application/json",
|
|
230
|
+
maxBytes: bytes.byteLength,
|
|
231
|
+
metadata: {
|
|
232
|
+
channel: value.channel,
|
|
233
|
+
...value.releaseId ? { releaseId: value.releaseId } : {},
|
|
234
|
+
sha256: digest(bytes)
|
|
235
|
+
},
|
|
236
|
+
signal
|
|
237
|
+
});
|
|
238
|
+
return value;
|
|
239
|
+
};
|
|
240
|
+
const promoteUpdate = async (input) => {
|
|
241
|
+
input.signal?.throwIfAborted();
|
|
242
|
+
if (input.rollout <= 0 || input.rollout > 1)
|
|
243
|
+
throw new MobileUpdateRegistryError("Mobile update rollout is invalid");
|
|
244
|
+
const release = await readManifest(input.appId, input.releaseId);
|
|
245
|
+
if (!release || release.manifest.channel !== input.channel)
|
|
246
|
+
throw new MobileUpdateRegistryError("Mobile update was not published to this channel");
|
|
247
|
+
const existing = await readChannel(input.appId, input.channel);
|
|
248
|
+
await writeChannel({
|
|
249
|
+
appId: input.appId,
|
|
250
|
+
channel: input.channel,
|
|
251
|
+
...existing?.releaseId && existing.releaseId !== input.releaseId ? { fallbackReleaseId: existing.releaseId } : existing?.fallbackReleaseId ? { fallbackReleaseId: existing.fallbackReleaseId } : {},
|
|
252
|
+
releaseId: input.releaseId,
|
|
253
|
+
rollout: input.rollout
|
|
254
|
+
}, input.signal);
|
|
255
|
+
return {
|
|
256
|
+
appId: input.appId,
|
|
257
|
+
channel: input.channel,
|
|
258
|
+
releaseId: input.releaseId,
|
|
259
|
+
rollout: input.rollout,
|
|
260
|
+
stage: "promoted"
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
return {
|
|
264
|
+
publishUpdate: async (input) => {
|
|
265
|
+
input.signal?.throwIfAborted();
|
|
266
|
+
const manifest = parseMobileUpdateManifest(input.manifest);
|
|
267
|
+
verifyManifestSignature(manifest, options.publicKeys);
|
|
268
|
+
const localRoot = path.resolve(input.releaseDirectory);
|
|
269
|
+
const localManifest = parseMobileUpdateManifest(JSON.parse(await readFile(path.join(localRoot, "update.json"), "utf8")));
|
|
270
|
+
if (JSON.stringify(localManifest) !== JSON.stringify(manifest))
|
|
271
|
+
throw new MobileUpdateRegistryError("Local mobile update manifest changed");
|
|
272
|
+
const existing = await readManifest(manifest.appId, manifest.releaseId);
|
|
273
|
+
let reused = existing !== null;
|
|
274
|
+
if (existing && JSON.stringify(existing.manifest) !== JSON.stringify(manifest))
|
|
275
|
+
throw new MobileUpdateRegistryError("Published mobile update is immutable");
|
|
276
|
+
if (!existing) {
|
|
277
|
+
for (const file of manifest.files) {
|
|
278
|
+
const local = path.join(localRoot, "files", file.path);
|
|
279
|
+
const metadata = await stat(local).catch(() => null);
|
|
280
|
+
if (!metadata?.isFile() || metadata.size !== file.bytes)
|
|
281
|
+
throw new MobileUpdateRegistryError(`Mobile update file ${file.path} size changed`);
|
|
282
|
+
if (await fileDigest(Bun.file(local)) !== file.sha256)
|
|
283
|
+
throw new MobileUpdateRegistryError(`Mobile update file ${file.path} integrity failed`);
|
|
284
|
+
const key = fileKey(manifest, file);
|
|
285
|
+
const stored = await options.store.head(key);
|
|
286
|
+
if (!stored) {
|
|
287
|
+
await options.store.put(key, Bun.file(local).stream(), {
|
|
288
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
289
|
+
contentType: "application/octet-stream",
|
|
290
|
+
maxBytes: file.bytes,
|
|
291
|
+
metadata: { releaseId: manifest.releaseId, sha256: file.sha256 },
|
|
292
|
+
signal: input.signal
|
|
293
|
+
});
|
|
294
|
+
} else if (stored.size !== file.bytes || stored.metadata?.sha256 !== file.sha256 || stored.metadata?.releaseId !== manifest.releaseId)
|
|
295
|
+
throw new MobileUpdateRegistryError("Stored mobile update file identity changed");
|
|
296
|
+
}
|
|
297
|
+
const bytes = json(manifest);
|
|
298
|
+
await options.store.put(manifestKey(manifest), bytes, {
|
|
299
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
300
|
+
contentType: "application/json",
|
|
301
|
+
maxBytes: bytes.byteLength,
|
|
302
|
+
metadata: { releaseId: manifest.releaseId, sha256: digest(bytes) },
|
|
303
|
+
signal: input.signal
|
|
304
|
+
});
|
|
305
|
+
if (!await readManifest(manifest.appId, manifest.releaseId))
|
|
306
|
+
throw new MobileUpdateRegistryError("Mobile update publication verification failed");
|
|
307
|
+
reused = false;
|
|
308
|
+
}
|
|
309
|
+
await promoteUpdate({
|
|
310
|
+
appId: manifest.appId,
|
|
311
|
+
channel: manifest.channel,
|
|
312
|
+
releaseId: manifest.releaseId,
|
|
313
|
+
rollout: input.rollout,
|
|
314
|
+
signal: input.signal
|
|
315
|
+
});
|
|
316
|
+
return {
|
|
317
|
+
appId: manifest.appId,
|
|
318
|
+
channel: manifest.channel,
|
|
319
|
+
releaseId: manifest.releaseId,
|
|
320
|
+
reused,
|
|
321
|
+
rollout: input.rollout,
|
|
322
|
+
stage: "published"
|
|
323
|
+
};
|
|
324
|
+
},
|
|
325
|
+
promoteUpdate,
|
|
326
|
+
rollbackUpdate: async (input) => {
|
|
327
|
+
input.signal?.throwIfAborted();
|
|
328
|
+
const existing = await readChannel(input.appId, input.channel);
|
|
329
|
+
if (!existing)
|
|
330
|
+
throw new MobileUpdateRegistryError("Mobile update channel does not exist");
|
|
331
|
+
if (input.releaseId) {
|
|
332
|
+
const release = await readManifest(input.appId, input.releaseId);
|
|
333
|
+
if (!release || release.manifest.channel !== input.channel)
|
|
334
|
+
throw new MobileUpdateRegistryError("Mobile rollback release was not published");
|
|
335
|
+
}
|
|
336
|
+
await writeChannel({
|
|
337
|
+
appId: input.appId,
|
|
338
|
+
channel: input.channel,
|
|
339
|
+
...existing.releaseId ? { fallbackReleaseId: existing.releaseId } : {},
|
|
340
|
+
...input.releaseId ? { releaseId: input.releaseId } : {},
|
|
341
|
+
rollout: input.releaseId ? 1 : 0
|
|
342
|
+
}, input.signal);
|
|
343
|
+
return {
|
|
344
|
+
appId: input.appId,
|
|
345
|
+
channel: input.channel,
|
|
346
|
+
...input.releaseId ? { releaseId: input.releaseId } : {},
|
|
347
|
+
stage: "rolled-back"
|
|
348
|
+
};
|
|
349
|
+
},
|
|
350
|
+
resolveUpdate: async (input) => {
|
|
351
|
+
const channel = await readChannel(input.appId, input.channel);
|
|
352
|
+
if (!channel?.releaseId)
|
|
353
|
+
return null;
|
|
354
|
+
const selected = rolloutMember({
|
|
355
|
+
appId: input.appId,
|
|
356
|
+
channel: input.channel,
|
|
357
|
+
installationId: input.installationId,
|
|
358
|
+
releaseId: channel.releaseId,
|
|
359
|
+
rollout: channel.rollout
|
|
360
|
+
}) ? channel.releaseId : channel.fallbackReleaseId;
|
|
361
|
+
if (!selected)
|
|
362
|
+
return null;
|
|
363
|
+
const release = await readManifest(input.appId, selected);
|
|
364
|
+
if (!release || release.manifest.runtimeFingerprint !== input.runtimeFingerprint)
|
|
365
|
+
return null;
|
|
366
|
+
return { manifest: release.manifest, manifestKey: release.key };
|
|
367
|
+
},
|
|
368
|
+
readUpdateFile: async (input) => {
|
|
369
|
+
const release = await readManifest(input.appId, input.releaseId);
|
|
370
|
+
if (!release)
|
|
371
|
+
return null;
|
|
372
|
+
const requested = safePath(input.path);
|
|
373
|
+
const file = release.manifest.files.find((candidate) => candidate.path === requested);
|
|
374
|
+
if (!file)
|
|
375
|
+
return null;
|
|
376
|
+
const key = fileKey(release.manifest, file);
|
|
377
|
+
const [bytes, head] = await Promise.all([
|
|
378
|
+
options.store.get(key),
|
|
379
|
+
options.store.head(key)
|
|
380
|
+
]);
|
|
381
|
+
if (!bytes || !head)
|
|
382
|
+
return null;
|
|
383
|
+
if (bytes.byteLength !== file.bytes || head.size !== file.bytes || head.metadata?.sha256 !== file.sha256 || head.metadata?.releaseId !== release.manifest.releaseId || digest(bytes) !== file.sha256)
|
|
384
|
+
throw new MobileUpdateRegistryError("Stored mobile update file integrity failed");
|
|
385
|
+
return { bytes, file };
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
};
|
|
389
|
+
var createMobileUpdateHandler = (options) => {
|
|
390
|
+
const route = (options.route ?? `/__absolute/mobile/updates/${options.channel}`).replace(/^\/+|\/+$/g, "");
|
|
391
|
+
const allowedOrigins = new Set(options.allowedOrigins ?? ["capacitor://localhost", "https://localhost"]);
|
|
392
|
+
return async (request) => {
|
|
393
|
+
const origin = request.headers.get("origin");
|
|
394
|
+
const cors = origin && allowedOrigins.has(origin) ? { "access-control-allow-origin": origin, vary: "Origin" } : {};
|
|
395
|
+
if (request.method === "OPTIONS") {
|
|
396
|
+
if (!origin || !allowedOrigins.has(origin))
|
|
397
|
+
return new Response(null, { status: 403 });
|
|
398
|
+
return new Response(null, {
|
|
399
|
+
headers: {
|
|
400
|
+
...cors,
|
|
401
|
+
"access-control-allow-headers": "x-absolute-mobile-app,x-absolute-mobile-channel,x-absolute-mobile-installation,x-absolute-mobile-release,x-absolute-mobile-runtime",
|
|
402
|
+
"access-control-allow-methods": "GET,OPTIONS",
|
|
403
|
+
"access-control-max-age": "600"
|
|
404
|
+
},
|
|
405
|
+
status: 204
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
if (request.method !== "GET")
|
|
409
|
+
return new Response(null, { status: 405 });
|
|
410
|
+
const pathname = new URL(request.url).pathname.replace(/^\/+/, "");
|
|
411
|
+
const relative = pathname.startsWith(`${route}/`) ? pathname.slice(route.length + 1) : "";
|
|
412
|
+
if (relative === "update.json") {
|
|
413
|
+
const appId = request.headers.get("x-absolute-mobile-app");
|
|
414
|
+
const channel = request.headers.get("x-absolute-mobile-channel");
|
|
415
|
+
const installationId = request.headers.get("x-absolute-mobile-installation");
|
|
416
|
+
const runtimeFingerprint = request.headers.get("x-absolute-mobile-runtime");
|
|
417
|
+
if (appId !== options.appId || channel !== options.channel || !installationId || !runtimeFingerprint)
|
|
418
|
+
return new Response(null, { status: 400 });
|
|
419
|
+
const selected = await options.registry.resolveUpdate({
|
|
420
|
+
appId,
|
|
421
|
+
channel,
|
|
422
|
+
installationId,
|
|
423
|
+
runtimeFingerprint
|
|
424
|
+
});
|
|
425
|
+
if (!selected)
|
|
426
|
+
return new Response(null, { status: 204 });
|
|
427
|
+
return Response.json(selected.manifest, {
|
|
428
|
+
headers: {
|
|
429
|
+
...cors,
|
|
430
|
+
"cache-control": "no-store",
|
|
431
|
+
etag: `"${selected.manifest.releaseId}"`
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
const match = /^(amu_[a-f0-9]{64})\/files\/(.+)$/.exec(relative);
|
|
436
|
+
if (!match?.[1] || !match[2])
|
|
437
|
+
return new Response(null, { status: 404 });
|
|
438
|
+
const file = await options.registry.readUpdateFile({
|
|
439
|
+
appId: options.appId,
|
|
440
|
+
path: decodeURIComponent(match[2]),
|
|
441
|
+
releaseId: match[1]
|
|
442
|
+
});
|
|
443
|
+
if (!file)
|
|
444
|
+
return new Response(null, { status: 404 });
|
|
445
|
+
return new Response(new Blob([new Uint8Array(file.bytes).buffer]), {
|
|
446
|
+
headers: {
|
|
447
|
+
...cors,
|
|
448
|
+
"cache-control": "public, max-age=31536000, immutable",
|
|
449
|
+
"content-length": String(file.file.bytes),
|
|
450
|
+
etag: `"${file.file.sha256}"`
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
};
|
|
454
|
+
};
|
|
455
|
+
export {
|
|
456
|
+
MOBILE_UPDATE_REGISTRY_FORMAT,
|
|
457
|
+
MobileUpdateRegistryError,
|
|
458
|
+
createMobileUpdateHandler,
|
|
459
|
+
createMobileUpdateRegistry,
|
|
460
|
+
parseMobileUpdateManifest
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
//# debugId=083E820827B573F264756E2164756E21
|
|
464
|
+
//# sourceMappingURL=mobileUpdate.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/mobileUpdate.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { createHash, createPublicKey, verify } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { NativeReleaseBlobStore } from \"./nativeRelease\";\n\nexport const MOBILE_UPDATE_REGISTRY_FORMAT = 1 as const;\nconst DEFAULT_PREFIX = \"absolutejs/mobile-updates\";\nconst MAX_FILE_BYTES = 32 * 1024 * 1024;\nconst MAX_TOTAL_BYTES = 128 * 1024 * 1024;\nconst HASH = /^[a-f0-9]{64}$/;\nconst RELEASE = /^amu_[a-f0-9]{64}$/;\nconst APP_ID = /^[A-Za-z][\\w]*(?:\\.[A-Za-z][\\w]*)+$/;\nconst NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;\n\nexport type MobileUpdateFile = {\n bytes: number;\n path: string;\n sha256: string;\n};\n\nexport type MobileUpdateManifest = {\n appId: string;\n channel: string;\n classification: \"bug-fix\" | \"content\" | \"security\";\n createdAt: string;\n files: MobileUpdateFile[];\n format: 1;\n releaseId: string;\n runtimeFingerprint: string;\n signature: {\n algorithm: \"ecdsa-p256-sha256\";\n keyId: string;\n value: string;\n };\n withinSubmittedPurpose: true;\n};\n\nexport type MobileUpdateChannel = {\n appId: string;\n channel: string;\n fallbackReleaseId?: string;\n format: typeof MOBILE_UPDATE_REGISTRY_FORMAT;\n promotedAt: string;\n releaseId?: string;\n rollout: number;\n};\n\nexport type MobileUpdatePublication = {\n appId: string;\n channel: string;\n releaseId: string;\n reused: boolean;\n rollout: number;\n stage: \"published\";\n};\n\nexport type MobileUpdatePromotion = {\n appId: string;\n channel: string;\n releaseId: string;\n rollout: number;\n stage: \"promoted\";\n};\n\nexport type MobileUpdateRollback = {\n appId: string;\n channel: string;\n releaseId?: string;\n stage: \"rolled-back\";\n};\n\nexport type MobileUpdateRegistry = {\n publishUpdate(input: {\n manifest: MobileUpdateManifest;\n releaseDirectory: string;\n rollout: number;\n signal?: AbortSignal;\n }): Promise<MobileUpdatePublication>;\n promoteUpdate(input: {\n appId: string;\n channel: string;\n releaseId: string;\n rollout: number;\n signal?: AbortSignal;\n }): Promise<MobileUpdatePromotion>;\n rollbackUpdate(input: {\n appId: string;\n channel: string;\n releaseId?: string;\n signal?: AbortSignal;\n }): Promise<MobileUpdateRollback>;\n resolveUpdate(input: {\n appId: string;\n channel: string;\n installationId: string;\n runtimeFingerprint: string;\n }): Promise<{ manifest: MobileUpdateManifest; manifestKey: string } | null>;\n readUpdateFile(input: {\n appId: string;\n path: string;\n releaseId: string;\n }): Promise<{ bytes: Uint8Array; file: MobileUpdateFile } | null>;\n};\n\nexport type MobileUpdateRegistryOptions = {\n clock?: () => Date;\n prefix?: string;\n /** Trusted ECDSA P-256 SPKI public keys as canonical base64 DER. */\n publicKeys: Readonly<Record<string, string>>;\n store: NativeReleaseBlobStore;\n};\n\nexport class MobileUpdateRegistryError extends Error {}\n\nconst object = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst text = (value: unknown, field: string) => {\n if (typeof value !== \"string\" || value.length === 0)\n throw new MobileUpdateRegistryError(`Mobile update ${field} is invalid`);\n\n return value;\n};\n\nconst iso = (value: unknown): value is string =>\n typeof value === \"string\" &&\n Number.isFinite(Date.parse(value)) &&\n new Date(value).toISOString() === value;\n\nconst safePath = (value: unknown) => {\n const file = text(value, \"file path\").replaceAll(\"\\\\\", \"/\");\n if (\n file.startsWith(\"/\") ||\n file\n .split(\"/\")\n .some((segment) => !segment || segment === \".\" || segment === \"..\")\n )\n throw new MobileUpdateRegistryError(\"Mobile update file path is invalid\");\n\n return file;\n};\n\nexport const parseMobileUpdateManifest = (\n value: unknown,\n): MobileUpdateManifest => {\n if (!object(value) || value.format !== 1)\n throw new MobileUpdateRegistryError(\"Mobile update manifest is invalid\");\n const appId = text(value.appId, \"appId\");\n const channel = text(value.channel, \"channel\");\n const releaseId = text(value.releaseId, \"releaseId\");\n const runtimeFingerprint = text(value.runtimeFingerprint, \"runtime\");\n if (!APP_ID.test(appId) || !NAME.test(channel) || !RELEASE.test(releaseId))\n throw new MobileUpdateRegistryError(\"Mobile update identity is invalid\");\n if (!HASH.test(runtimeFingerprint) || !iso(value.createdAt))\n throw new MobileUpdateRegistryError(\n \"Mobile update runtime or timestamp is invalid\",\n );\n if (\n value.classification !== \"bug-fix\" &&\n value.classification !== \"content\" &&\n value.classification !== \"security\"\n )\n throw new MobileUpdateRegistryError(\n \"Mobile update classification is invalid\",\n );\n if (value.withinSubmittedPurpose !== true)\n throw new MobileUpdateRegistryError(\n \"Mobile update policy attestation is missing\",\n );\n if (!Array.isArray(value.files) || value.files.length === 0)\n throw new MobileUpdateRegistryError(\n \"Mobile update file inventory is invalid\",\n );\n const files = value.files.map((candidate): MobileUpdateFile => {\n if (!object(candidate))\n throw new MobileUpdateRegistryError(\"Mobile update file is invalid\");\n const filePath = safePath(candidate.path);\n if (\n !Number.isSafeInteger(candidate.bytes) ||\n Number(candidate.bytes) < 0 ||\n Number(candidate.bytes) > MAX_FILE_BYTES ||\n typeof candidate.sha256 !== \"string\" ||\n !HASH.test(candidate.sha256)\n )\n throw new MobileUpdateRegistryError(\n `Mobile update file ${filePath} is invalid`,\n );\n\n return {\n bytes: Number(candidate.bytes),\n path: filePath,\n sha256: candidate.sha256,\n };\n });\n if (\n files.reduce((total, file) => total + file.bytes, 0) > MAX_TOTAL_BYTES ||\n new Set(files.map((file) => file.path)).size !== files.length ||\n files.some((file, index) =>\n index > 0\n ? file.path.localeCompare(files[index - 1]?.path ?? \"\") <= 0\n : false,\n )\n )\n throw new MobileUpdateRegistryError(\n \"Mobile update file inventory is invalid\",\n );\n const signatureKeyId = object(value.signature)\n ? text(value.signature.keyId, \"signature key\")\n : \"\";\n const signatureValue = object(value.signature)\n ? text(value.signature.value, \"signature\")\n : \"\";\n if (\n !object(value.signature) ||\n value.signature.algorithm !== \"ecdsa-p256-sha256\" ||\n !NAME.test(signatureKeyId) ||\n !/^[A-Za-z0-9+/]+={0,2}$/.test(signatureValue) ||\n Buffer.from(signatureValue, \"base64\").byteLength !== 64 ||\n Buffer.from(signatureValue, \"base64\").toString(\"base64\") !== signatureValue\n )\n throw new MobileUpdateRegistryError(\"Mobile update signature is invalid\");\n\n return {\n appId,\n channel,\n classification: value.classification,\n createdAt: value.createdAt,\n files,\n format: 1,\n releaseId,\n runtimeFingerprint,\n signature: {\n algorithm: \"ecdsa-p256-sha256\",\n keyId: signatureKeyId,\n value: signatureValue,\n },\n withinSubmittedPurpose: true,\n };\n};\n\nconst canonicalValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(canonicalValue);\n if (!object(value)) return value;\n\n return Object.fromEntries(\n Object.keys(value)\n .sort()\n .map((key) => [key, canonicalValue(value[key])]),\n );\n};\n\nconst verifyManifestSignature = (\n manifest: MobileUpdateManifest,\n publicKeys: Readonly<Record<string, string>>,\n) => {\n const encoded = publicKeys[manifest.signature.keyId];\n if (!encoded)\n throw new MobileUpdateRegistryError(\n \"Mobile update signing key is not trusted\",\n );\n let publicKey;\n try {\n const der = Buffer.from(encoded, \"base64\");\n if (der.toString(\"base64\") !== encoded) throw new Error(\"invalid base64\");\n publicKey = createPublicKey({ format: \"der\", key: der, type: \"spki\" });\n } catch {\n throw new MobileUpdateRegistryError(\n \"Mobile update trusted public key is invalid\",\n );\n }\n if (\n publicKey.asymmetricKeyType !== \"ec\" ||\n publicKey.asymmetricKeyDetails?.namedCurve !== \"prime256v1\"\n )\n throw new MobileUpdateRegistryError(\n \"Mobile update trusted public key must use ECDSA P-256\",\n );\n const { signature: _signature, ...unsigned } = manifest;\n if (\n !verify(\n \"sha256\",\n new TextEncoder().encode(JSON.stringify(canonicalValue(unsigned))),\n { dsaEncoding: \"ieee-p1363\", key: publicKey },\n Buffer.from(manifest.signature.value, \"base64\"),\n )\n )\n throw new MobileUpdateRegistryError(\n \"Mobile update signature verification failed\",\n );\n};\n\nconst parseChannel = (value: unknown): MobileUpdateChannel => {\n if (!object(value) || value.format !== MOBILE_UPDATE_REGISTRY_FORMAT)\n throw new MobileUpdateRegistryError(\"Mobile update channel is invalid\");\n const appId = text(value.appId, \"channel appId\");\n const channel = text(value.channel, \"channel\");\n if (\n !APP_ID.test(appId) ||\n !NAME.test(channel) ||\n !iso(value.promotedAt) ||\n typeof value.rollout !== \"number\" ||\n value.rollout < 0 ||\n value.rollout > 1 ||\n (value.releaseId !== undefined &&\n (typeof value.releaseId !== \"string\" ||\n !RELEASE.test(value.releaseId))) ||\n (value.fallbackReleaseId !== undefined &&\n (typeof value.fallbackReleaseId !== \"string\" ||\n !RELEASE.test(value.fallbackReleaseId)))\n )\n throw new MobileUpdateRegistryError(\"Mobile update channel is invalid\");\n\n return {\n appId,\n channel,\n ...(value.fallbackReleaseId\n ? { fallbackReleaseId: value.fallbackReleaseId }\n : {}),\n format: MOBILE_UPDATE_REGISTRY_FORMAT,\n promotedAt: value.promotedAt,\n ...(value.releaseId ? { releaseId: value.releaseId } : {}),\n rollout: value.rollout,\n };\n};\n\nconst normalizedPrefix = (value: string) => {\n const prefix = value.replace(/^\\/+|\\/+$/g, \"\");\n if (\n !prefix ||\n prefix.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n )\n throw new MobileUpdateRegistryError(\"Mobile update prefix is invalid\");\n\n return prefix;\n};\n\nconst appHash = (appId: string) =>\n createHash(\"sha256\").update(appId).digest(\"hex\");\nconst digest = (bytes: Uint8Array) =>\n createHash(\"sha256\").update(bytes).digest(\"hex\");\nconst json = (value: unknown) =>\n new TextEncoder().encode(`${JSON.stringify(value, null, 2)}\\n`);\nconst decode = (value: Uint8Array) =>\n JSON.parse(new TextDecoder().decode(value)) as unknown;\nconst fileDigest = async (file: Bun.BunFile) => {\n const hasher = new Bun.CryptoHasher(\"sha256\");\n for await (const chunk of file.stream()) hasher.update(chunk);\n\n return hasher.digest(\"hex\");\n};\n\nconst rolloutMember = (input: {\n appId: string;\n channel: string;\n installationId: string;\n releaseId: string;\n rollout: number;\n}) => {\n if (input.rollout === 0) return false;\n if (input.rollout === 1) return true;\n const value = createHash(\"sha256\")\n .update(\n `${input.appId}\\0${input.channel}\\0${input.releaseId}\\0${input.installationId}`,\n )\n .digest()\n .readUInt32BE(0);\n\n return value / 0x1_0000_0000 < input.rollout;\n};\n\nexport const createMobileUpdateRegistry = (\n options: MobileUpdateRegistryOptions,\n): MobileUpdateRegistry => {\n const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);\n const clock = options.clock ?? (() => new Date());\n const root = (appId: string) => `${prefix}/${appHash(appId)}`;\n const releaseRoot = (\n manifest: Pick<MobileUpdateManifest, \"appId\" | \"releaseId\">,\n ) => `${root(manifest.appId)}/releases/${manifest.releaseId}`;\n const manifestKey = (\n manifest: Pick<MobileUpdateManifest, \"appId\" | \"releaseId\">,\n ) => `${releaseRoot(manifest)}/update.json`;\n const fileKey = (\n manifest: Pick<MobileUpdateManifest, \"appId\" | \"releaseId\">,\n file: MobileUpdateFile,\n ) => `${releaseRoot(manifest)}/files/${file.path}`;\n const channelKey = (appId: string, channel: string) => {\n if (!APP_ID.test(appId) || !NAME.test(channel))\n throw new MobileUpdateRegistryError(\n \"Mobile update channel identity is invalid\",\n );\n\n return `${root(appId)}/channels/${channel}.json`;\n };\n const readManifest = async (appId: string, releaseId: string) => {\n const key = manifestKey({ appId, releaseId });\n const bytes = await options.store.get(key);\n if (!bytes) return null;\n const head = await options.store.head(key);\n if (\n !head ||\n head.size !== bytes.byteLength ||\n head.metadata?.sha256 !== digest(bytes)\n )\n throw new MobileUpdateRegistryError(\n \"Stored mobile update manifest integrity failed\",\n );\n const manifest = parseMobileUpdateManifest(decode(bytes));\n verifyManifestSignature(manifest, options.publicKeys);\n if (manifest.appId !== appId || manifest.releaseId !== releaseId)\n throw new MobileUpdateRegistryError(\n \"Stored mobile update identity changed\",\n );\n\n return { key, manifest };\n };\n const readChannel = async (appId: string, channel: string) => {\n const bytes = await options.store.get(channelKey(appId, channel));\n if (!bytes) return null;\n const value = parseChannel(decode(bytes));\n if (value.appId !== appId || value.channel !== channel)\n throw new MobileUpdateRegistryError(\n \"Stored mobile update channel identity changed\",\n );\n\n return value;\n };\n const writeChannel = async (\n input: Omit<MobileUpdateChannel, \"format\" | \"promotedAt\">,\n signal?: AbortSignal,\n ) => {\n const value: MobileUpdateChannel = {\n ...input,\n format: MOBILE_UPDATE_REGISTRY_FORMAT,\n promotedAt: clock().toISOString(),\n };\n const bytes = json(value);\n await options.store.put(channelKey(value.appId, value.channel), bytes, {\n cacheControl: \"no-cache\",\n contentType: \"application/json\",\n maxBytes: bytes.byteLength,\n metadata: {\n channel: value.channel,\n ...(value.releaseId ? { releaseId: value.releaseId } : {}),\n sha256: digest(bytes),\n },\n signal,\n });\n\n return value;\n };\n const promoteUpdate: MobileUpdateRegistry[\"promoteUpdate\"] = async (\n input,\n ) => {\n input.signal?.throwIfAborted();\n if (input.rollout <= 0 || input.rollout > 1)\n throw new MobileUpdateRegistryError(\"Mobile update rollout is invalid\");\n const release = await readManifest(input.appId, input.releaseId);\n if (!release || release.manifest.channel !== input.channel)\n throw new MobileUpdateRegistryError(\n \"Mobile update was not published to this channel\",\n );\n const existing = await readChannel(input.appId, input.channel);\n await writeChannel(\n {\n appId: input.appId,\n channel: input.channel,\n ...(existing?.releaseId && existing.releaseId !== input.releaseId\n ? { fallbackReleaseId: existing.releaseId }\n : existing?.fallbackReleaseId\n ? { fallbackReleaseId: existing.fallbackReleaseId }\n : {}),\n releaseId: input.releaseId,\n rollout: input.rollout,\n },\n input.signal,\n );\n\n return {\n appId: input.appId,\n channel: input.channel,\n releaseId: input.releaseId,\n rollout: input.rollout,\n stage: \"promoted\",\n };\n };\n\n return {\n publishUpdate: async (input) => {\n input.signal?.throwIfAborted();\n const manifest = parseMobileUpdateManifest(input.manifest);\n verifyManifestSignature(manifest, options.publicKeys);\n const localRoot = path.resolve(input.releaseDirectory);\n const localManifest = parseMobileUpdateManifest(\n JSON.parse(await readFile(path.join(localRoot, \"update.json\"), \"utf8\")),\n );\n if (JSON.stringify(localManifest) !== JSON.stringify(manifest))\n throw new MobileUpdateRegistryError(\n \"Local mobile update manifest changed\",\n );\n const existing = await readManifest(manifest.appId, manifest.releaseId);\n let reused = existing !== null;\n if (\n existing &&\n JSON.stringify(existing.manifest) !== JSON.stringify(manifest)\n )\n throw new MobileUpdateRegistryError(\n \"Published mobile update is immutable\",\n );\n if (!existing) {\n for (const file of manifest.files) {\n const local = path.join(localRoot, \"files\", file.path);\n const metadata = await stat(local).catch(() => null);\n if (!metadata?.isFile() || metadata.size !== file.bytes)\n throw new MobileUpdateRegistryError(\n `Mobile update file ${file.path} size changed`,\n );\n if ((await fileDigest(Bun.file(local))) !== file.sha256)\n throw new MobileUpdateRegistryError(\n `Mobile update file ${file.path} integrity failed`,\n );\n const key = fileKey(manifest, file);\n const stored = await options.store.head(key);\n if (!stored) {\n await options.store.put(key, Bun.file(local).stream(), {\n cacheControl: \"public, max-age=31536000, immutable\",\n contentType: \"application/octet-stream\",\n maxBytes: file.bytes,\n metadata: { releaseId: manifest.releaseId, sha256: file.sha256 },\n signal: input.signal,\n });\n } else if (\n stored.size !== file.bytes ||\n stored.metadata?.sha256 !== file.sha256 ||\n stored.metadata?.releaseId !== manifest.releaseId\n )\n throw new MobileUpdateRegistryError(\n \"Stored mobile update file identity changed\",\n );\n }\n const bytes = json(manifest);\n await options.store.put(manifestKey(manifest), bytes, {\n cacheControl: \"public, max-age=31536000, immutable\",\n contentType: \"application/json\",\n maxBytes: bytes.byteLength,\n metadata: { releaseId: manifest.releaseId, sha256: digest(bytes) },\n signal: input.signal,\n });\n if (!(await readManifest(manifest.appId, manifest.releaseId)))\n throw new MobileUpdateRegistryError(\n \"Mobile update publication verification failed\",\n );\n reused = false;\n }\n await promoteUpdate({\n appId: manifest.appId,\n channel: manifest.channel,\n releaseId: manifest.releaseId,\n rollout: input.rollout,\n signal: input.signal,\n });\n\n return {\n appId: manifest.appId,\n channel: manifest.channel,\n releaseId: manifest.releaseId,\n reused,\n rollout: input.rollout,\n stage: \"published\",\n };\n },\n promoteUpdate,\n rollbackUpdate: async (input) => {\n input.signal?.throwIfAborted();\n const existing = await readChannel(input.appId, input.channel);\n if (!existing)\n throw new MobileUpdateRegistryError(\n \"Mobile update channel does not exist\",\n );\n if (input.releaseId) {\n const release = await readManifest(input.appId, input.releaseId);\n if (!release || release.manifest.channel !== input.channel)\n throw new MobileUpdateRegistryError(\n \"Mobile rollback release was not published\",\n );\n }\n await writeChannel(\n {\n appId: input.appId,\n channel: input.channel,\n ...(existing.releaseId\n ? { fallbackReleaseId: existing.releaseId }\n : {}),\n ...(input.releaseId ? { releaseId: input.releaseId } : {}),\n rollout: input.releaseId ? 1 : 0,\n },\n input.signal,\n );\n\n return {\n appId: input.appId,\n channel: input.channel,\n ...(input.releaseId ? { releaseId: input.releaseId } : {}),\n stage: \"rolled-back\",\n };\n },\n resolveUpdate: async (input) => {\n const channel = await readChannel(input.appId, input.channel);\n if (!channel?.releaseId) return null;\n const selected = rolloutMember({\n appId: input.appId,\n channel: input.channel,\n installationId: input.installationId,\n releaseId: channel.releaseId,\n rollout: channel.rollout,\n })\n ? channel.releaseId\n : channel.fallbackReleaseId;\n if (!selected) return null;\n const release = await readManifest(input.appId, selected);\n if (\n !release ||\n release.manifest.runtimeFingerprint !== input.runtimeFingerprint\n )\n return null;\n\n return { manifest: release.manifest, manifestKey: release.key };\n },\n readUpdateFile: async (input) => {\n const release = await readManifest(input.appId, input.releaseId);\n if (!release) return null;\n const requested = safePath(input.path);\n const file = release.manifest.files.find(\n (candidate) => candidate.path === requested,\n );\n if (!file) return null;\n const key = fileKey(release.manifest, file);\n const [bytes, head] = await Promise.all([\n options.store.get(key),\n options.store.head(key),\n ]);\n if (!bytes || !head) return null;\n if (\n bytes.byteLength !== file.bytes ||\n head.size !== file.bytes ||\n head.metadata?.sha256 !== file.sha256 ||\n head.metadata?.releaseId !== release.manifest.releaseId ||\n digest(bytes) !== file.sha256\n )\n throw new MobileUpdateRegistryError(\n \"Stored mobile update file integrity failed\",\n );\n\n return { bytes, file };\n },\n };\n};\n\nexport const createMobileUpdateHandler = (options: {\n allowedOrigins?: readonly string[];\n appId: string;\n channel: string;\n registry: MobileUpdateRegistry;\n route?: string;\n}) => {\n const route = (\n options.route ?? `/__absolute/mobile/updates/${options.channel}`\n ).replace(/^\\/+|\\/+$/g, \"\");\n const allowedOrigins = new Set(\n options.allowedOrigins ?? [\"capacitor://localhost\", \"https://localhost\"],\n );\n\n return async (request: Request) => {\n const origin = request.headers.get(\"origin\");\n const cors: Record<string, string> =\n origin && allowedOrigins.has(origin)\n ? { \"access-control-allow-origin\": origin, vary: \"Origin\" }\n : {};\n if (request.method === \"OPTIONS\") {\n if (!origin || !allowedOrigins.has(origin))\n return new Response(null, { status: 403 });\n\n return new Response(null, {\n headers: {\n ...cors,\n \"access-control-allow-headers\":\n \"x-absolute-mobile-app,x-absolute-mobile-channel,x-absolute-mobile-installation,x-absolute-mobile-release,x-absolute-mobile-runtime\",\n \"access-control-allow-methods\": \"GET,OPTIONS\",\n \"access-control-max-age\": \"600\",\n },\n status: 204,\n });\n }\n if (request.method !== \"GET\") return new Response(null, { status: 405 });\n const pathname = new URL(request.url).pathname.replace(/^\\/+/, \"\");\n const relative = pathname.startsWith(`${route}/`)\n ? pathname.slice(route.length + 1)\n : \"\";\n if (relative === \"update.json\") {\n const appId = request.headers.get(\"x-absolute-mobile-app\");\n const channel = request.headers.get(\"x-absolute-mobile-channel\");\n const installationId = request.headers.get(\n \"x-absolute-mobile-installation\",\n );\n const runtimeFingerprint = request.headers.get(\n \"x-absolute-mobile-runtime\",\n );\n if (\n appId !== options.appId ||\n channel !== options.channel ||\n !installationId ||\n !runtimeFingerprint\n )\n return new Response(null, { status: 400 });\n const selected = await options.registry.resolveUpdate({\n appId,\n channel,\n installationId,\n runtimeFingerprint,\n });\n if (!selected) return new Response(null, { status: 204 });\n\n return Response.json(selected.manifest, {\n headers: {\n ...cors,\n \"cache-control\": \"no-store\",\n etag: `\"${selected.manifest.releaseId}\"`,\n },\n });\n }\n const match = /^(amu_[a-f0-9]{64})\\/files\\/(.+)$/.exec(relative);\n if (!match?.[1] || !match[2]) return new Response(null, { status: 404 });\n const file = await options.registry.readUpdateFile({\n appId: options.appId,\n path: decodeURIComponent(match[2]),\n releaseId: match[1],\n });\n if (!file) return new Response(null, { status: 404 });\n\n return new Response(new Blob([new Uint8Array(file.bytes).buffer]), {\n headers: {\n ...cors,\n \"cache-control\": \"public, max-age=31536000, immutable\",\n \"content-length\": String(file.file.bytes),\n etag: `\"${file.file.sha256}\"`,\n },\n });\n };\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "083E820827B573F264756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/nativeRelease.js
CHANGED
|
@@ -19,12 +19,14 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
19
19
|
}
|
|
20
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
21
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(mod))
|
|
24
|
+
if (!__hasOwnProp.call(to, key))
|
|
25
|
+
__defProp(to, key, {
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
27
|
+
enumerable: true
|
|
28
|
+
});
|
|
29
|
+
}
|
|
28
30
|
if (canCache)
|
|
29
31
|
cache.set(mod, to);
|
|
30
32
|
return to;
|
|
@@ -374,10 +376,10 @@ var createNativeReleaseRegistry = (options) => {
|
|
|
374
376
|
};
|
|
375
377
|
};
|
|
376
378
|
export {
|
|
377
|
-
|
|
379
|
+
NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
378
380
|
NativeReleaseRegistryError,
|
|
379
|
-
|
|
381
|
+
createNativeReleaseRegistry
|
|
380
382
|
};
|
|
381
383
|
|
|
382
|
-
//# debugId=
|
|
384
|
+
//# debugId=6862EDA92AEFFC3564756E2164756E21
|
|
383
385
|
//# sourceMappingURL=nativeRelease.js.map
|