@absolutejs/deploy 0.21.1 → 0.22.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 +36 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +306 -2
- package/dist/index.js.map +5 -4
- package/dist/nativeRelease.d.ts +89 -0
- package/dist/nativeRelease.js +343 -0
- package/dist/nativeRelease.js.map +10 -0
- package/dist/releaseArtifact.js +1 -2
- package/dist/releaseArtifact.js.map +3 -3
- package/package.json +7 -2
|
@@ -0,0 +1,343 @@
|
|
|
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
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __require = import.meta.require;
|
|
34
|
+
|
|
35
|
+
// src/nativeRelease.ts
|
|
36
|
+
import { createHash } from "crypto";
|
|
37
|
+
import { readFile, stat } from "fs/promises";
|
|
38
|
+
import path from "path";
|
|
39
|
+
var DEFAULT_PREFIX = "absolutejs/native-releases";
|
|
40
|
+
var DEFAULT_MAX_ARTIFACT_BYTES = 2147483648;
|
|
41
|
+
var NATIVE_RELEASE_REGISTRY_FORMAT = 1;
|
|
42
|
+
var SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
43
|
+
var APP_ID_PATTERN = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
44
|
+
var CHANNEL_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
45
|
+
|
|
46
|
+
class NativeReleaseRegistryError extends Error {
|
|
47
|
+
}
|
|
48
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49
|
+
var requireString = (value, field) => {
|
|
50
|
+
if (typeof value !== "string" || value.length === 0)
|
|
51
|
+
throw new NativeReleaseRegistryError(`Native release ${field} is invalid`);
|
|
52
|
+
return value;
|
|
53
|
+
};
|
|
54
|
+
var parseMetadata = (value) => {
|
|
55
|
+
if (!isRecord(value))
|
|
56
|
+
throw new NativeReleaseRegistryError("Native release metadata is invalid");
|
|
57
|
+
const appId = requireString(value.appId, "appId");
|
|
58
|
+
const sha256 = requireString(value.sha256, "sha256");
|
|
59
|
+
const releaseId = requireString(value.releaseId, "releaseId");
|
|
60
|
+
if (!APP_ID_PATTERN.test(appId))
|
|
61
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
62
|
+
if (!SHA256_PATTERN.test(sha256))
|
|
63
|
+
throw new NativeReleaseRegistryError("Native release sha256 is invalid");
|
|
64
|
+
if (releaseId !== `amobile_android_${sha256}`)
|
|
65
|
+
throw new NativeReleaseRegistryError("Native release id does not match its artifact digest");
|
|
66
|
+
if (value.artifact !== "app-release.aab" || value.engine !== "capacitor" || value.format !== 1 || value.platform !== "android" || value.type !== "aab" || typeof value.signed !== "boolean" || !Number.isSafeInteger(value.bytes) || Number(value.bytes) < 1)
|
|
67
|
+
throw new NativeReleaseRegistryError("Native release metadata is invalid");
|
|
68
|
+
return {
|
|
69
|
+
appBuild: requireString(value.appBuild, "appBuild"),
|
|
70
|
+
appId,
|
|
71
|
+
artifact: "app-release.aab",
|
|
72
|
+
bytes: Number(value.bytes),
|
|
73
|
+
engine: "capacitor",
|
|
74
|
+
format: 1,
|
|
75
|
+
platform: "android",
|
|
76
|
+
releaseId,
|
|
77
|
+
runtime: requireString(value.runtime, "runtime"),
|
|
78
|
+
sha256,
|
|
79
|
+
signed: value.signed,
|
|
80
|
+
type: "aab"
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
var normalizedPrefix = (value) => {
|
|
84
|
+
const prefix = value.replace(/^\/+|\/+$/g, "");
|
|
85
|
+
if (prefix.length === 0 || prefix.split("/").some((segment) => segment === "." || segment === ".."))
|
|
86
|
+
throw new NativeReleaseRegistryError("Native release prefix is invalid");
|
|
87
|
+
return prefix;
|
|
88
|
+
};
|
|
89
|
+
var requireChannel = (value) => {
|
|
90
|
+
if (!CHANNEL_PATTERN.test(value))
|
|
91
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
92
|
+
return value;
|
|
93
|
+
};
|
|
94
|
+
var appIdentity = (appId) => createHash("sha256").update(appId).digest("hex");
|
|
95
|
+
var sha256Bytes = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
96
|
+
var isIsoTimestamp = (value) => {
|
|
97
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value)))
|
|
98
|
+
return false;
|
|
99
|
+
return new Date(value).toISOString() === value;
|
|
100
|
+
};
|
|
101
|
+
var sha256File = async (file) => {
|
|
102
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
103
|
+
for await (const chunk of file.stream())
|
|
104
|
+
hasher.update(chunk);
|
|
105
|
+
return hasher.digest("hex");
|
|
106
|
+
};
|
|
107
|
+
var encodedJson = (value) => new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
|
|
108
|
+
`);
|
|
109
|
+
var decodedJson = (bytes) => JSON.parse(new TextDecoder().decode(bytes));
|
|
110
|
+
var sameMetadata = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
111
|
+
var parseRecord = (value) => {
|
|
112
|
+
if (!isRecord(value) || value.format !== NATIVE_RELEASE_REGISTRY_FORMAT || typeof value.artifactKey !== "string")
|
|
113
|
+
throw new NativeReleaseRegistryError("Native release record is invalid");
|
|
114
|
+
const metadata = parseMetadata(value.metadata);
|
|
115
|
+
return {
|
|
116
|
+
artifactKey: value.artifactKey,
|
|
117
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
118
|
+
metadata
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
var parseChannel = (value) => {
|
|
122
|
+
if (!isRecord(value) || value.format !== NATIVE_RELEASE_REGISTRY_FORMAT || value.platform !== "android" || !isIsoTimestamp(value.promotedAt))
|
|
123
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
124
|
+
const appId = requireString(value.appId, "channel appId");
|
|
125
|
+
const channel = requireChannel(requireString(value.channel, "channel"));
|
|
126
|
+
const sha256 = requireString(value.sha256, "channel sha256");
|
|
127
|
+
const releaseId = requireString(value.releaseId, "channel releaseId");
|
|
128
|
+
if (!APP_ID_PATTERN.test(appId) || !SHA256_PATTERN.test(sha256))
|
|
129
|
+
throw new NativeReleaseRegistryError("Native release channel is invalid");
|
|
130
|
+
if (releaseId !== `amobile_android_${sha256}`)
|
|
131
|
+
throw new NativeReleaseRegistryError("Native release channel identity does not match");
|
|
132
|
+
return {
|
|
133
|
+
appId,
|
|
134
|
+
channel,
|
|
135
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
136
|
+
platform: "android",
|
|
137
|
+
promotedAt: value.promotedAt,
|
|
138
|
+
releaseId,
|
|
139
|
+
sha256
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
var createNativeReleaseRegistry = (options) => {
|
|
143
|
+
const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);
|
|
144
|
+
const maxArtifactBytes = options.maxArtifactBytes ?? DEFAULT_MAX_ARTIFACT_BYTES;
|
|
145
|
+
if (!Number.isSafeInteger(maxArtifactBytes) || maxArtifactBytes < 1)
|
|
146
|
+
throw new NativeReleaseRegistryError("Native release maxArtifactBytes is invalid");
|
|
147
|
+
const clock = options.clock ?? (() => new Date);
|
|
148
|
+
const appRoot = (appId, platform) => `${prefix}/${appIdentity(appId)}/${platform}`;
|
|
149
|
+
const releaseRoot = (metadata) => `${appRoot(metadata.appId, metadata.platform)}/releases/${metadata.releaseId}`;
|
|
150
|
+
const recordKey = (metadata) => `${releaseRoot(metadata)}/release.json`;
|
|
151
|
+
const artifactKey = (metadata) => `${releaseRoot(metadata)}/${metadata.artifact}`;
|
|
152
|
+
const channelKey = (appId, platform, channel) => `${appRoot(appId, platform)}/channels/${requireChannel(channel)}.json`;
|
|
153
|
+
const requireStoredArtifact = async (key, metadata) => {
|
|
154
|
+
const stored = await options.store.head(key);
|
|
155
|
+
if (!stored || stored.size !== metadata.bytes || stored.metadata?.sha256 !== metadata.sha256 || stored.metadata?.releaseId !== metadata.releaseId)
|
|
156
|
+
throw new NativeReleaseRegistryError("Stored native release artifact does not match its immutable identity");
|
|
157
|
+
};
|
|
158
|
+
const read = async (input) => {
|
|
159
|
+
if (!APP_ID_PATTERN.test(input.appId))
|
|
160
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
161
|
+
const digest = input.releaseId.replace(/^amobile_android_/, "");
|
|
162
|
+
if (!SHA256_PATTERN.test(digest) || input.releaseId !== `amobile_android_${digest}`)
|
|
163
|
+
throw new NativeReleaseRegistryError("Native release id is invalid");
|
|
164
|
+
const identity = {
|
|
165
|
+
appBuild: "lookup",
|
|
166
|
+
appId: input.appId,
|
|
167
|
+
artifact: "app-release.aab",
|
|
168
|
+
bytes: 1,
|
|
169
|
+
engine: "capacitor",
|
|
170
|
+
format: 1,
|
|
171
|
+
platform: "android",
|
|
172
|
+
releaseId: input.releaseId,
|
|
173
|
+
runtime: "lookup",
|
|
174
|
+
sha256: digest,
|
|
175
|
+
signed: true,
|
|
176
|
+
type: "aab"
|
|
177
|
+
};
|
|
178
|
+
const key = recordKey(identity);
|
|
179
|
+
const bytes = await options.store.get(key);
|
|
180
|
+
if (!bytes)
|
|
181
|
+
return null;
|
|
182
|
+
const storedRecord = await options.store.head(key);
|
|
183
|
+
if (!storedRecord || storedRecord.size !== bytes.byteLength || storedRecord.metadata?.releaseId !== input.releaseId || storedRecord.metadata?.sha256 !== sha256Bytes(bytes))
|
|
184
|
+
throw new NativeReleaseRegistryError("Stored native release record does not match its immutable identity");
|
|
185
|
+
const record = parseRecord(decodedJson(bytes));
|
|
186
|
+
if (record.metadata.appId !== input.appId || record.metadata.platform !== input.platform || record.metadata.releaseId !== input.releaseId || record.artifactKey !== artifactKey(record.metadata))
|
|
187
|
+
throw new NativeReleaseRegistryError("Stored native release record identity does not match");
|
|
188
|
+
await requireStoredArtifact(record.artifactKey, record.metadata);
|
|
189
|
+
return record;
|
|
190
|
+
};
|
|
191
|
+
const promote = async (input) => {
|
|
192
|
+
input.signal?.throwIfAborted();
|
|
193
|
+
const record = await read(input);
|
|
194
|
+
if (!record)
|
|
195
|
+
throw new NativeReleaseRegistryError("Native release was not published");
|
|
196
|
+
if (!record.metadata.signed && !input.allowUnsigned)
|
|
197
|
+
throw new NativeReleaseRegistryError("Unsigned native releases cannot be promoted");
|
|
198
|
+
const key = channelKey(input.appId, input.platform, input.channel);
|
|
199
|
+
const existingBytes = await options.store.get(key);
|
|
200
|
+
if (existingBytes) {
|
|
201
|
+
const existing = parseChannel(decodedJson(existingBytes));
|
|
202
|
+
if (existing.appId !== input.appId || existing.platform !== input.platform || existing.channel !== input.channel)
|
|
203
|
+
throw new NativeReleaseRegistryError("Stored native release channel identity does not match");
|
|
204
|
+
if (existing.releaseId === input.releaseId)
|
|
205
|
+
return existing;
|
|
206
|
+
}
|
|
207
|
+
const channel = {
|
|
208
|
+
appId: input.appId,
|
|
209
|
+
channel: requireChannel(input.channel),
|
|
210
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
211
|
+
platform: input.platform,
|
|
212
|
+
promotedAt: clock().toISOString(),
|
|
213
|
+
releaseId: record.metadata.releaseId,
|
|
214
|
+
sha256: record.metadata.sha256
|
|
215
|
+
};
|
|
216
|
+
const serialized = encodedJson(channel);
|
|
217
|
+
await options.store.put(key, serialized, {
|
|
218
|
+
cacheControl: "no-cache",
|
|
219
|
+
contentType: "application/json",
|
|
220
|
+
maxBytes: serialized.byteLength,
|
|
221
|
+
metadata: {
|
|
222
|
+
channel: channel.channel,
|
|
223
|
+
releaseId: channel.releaseId,
|
|
224
|
+
sha256: channel.sha256
|
|
225
|
+
},
|
|
226
|
+
signal: input.signal
|
|
227
|
+
});
|
|
228
|
+
const stored = await options.store.get(key);
|
|
229
|
+
if (!stored || JSON.stringify(parseChannel(decodedJson(stored))) !== JSON.stringify(channel))
|
|
230
|
+
throw new NativeReleaseRegistryError("Native release channel verification failed");
|
|
231
|
+
return channel;
|
|
232
|
+
};
|
|
233
|
+
return {
|
|
234
|
+
promote,
|
|
235
|
+
publish: async (input) => {
|
|
236
|
+
input.signal?.throwIfAborted();
|
|
237
|
+
const localRoot = path.resolve(input.releaseRoot);
|
|
238
|
+
const metadata = parseMetadata(JSON.parse(await readFile(path.join(localRoot, "release.json"), "utf8")));
|
|
239
|
+
if (metadata.bytes > maxArtifactBytes)
|
|
240
|
+
throw new NativeReleaseRegistryError("Native release exceeds the configured artifact limit");
|
|
241
|
+
if (!metadata.signed && !input.allowUnsigned)
|
|
242
|
+
throw new NativeReleaseRegistryError("Unsigned native releases cannot be published");
|
|
243
|
+
const localArtifact = path.join(localRoot, metadata.artifact);
|
|
244
|
+
const artifactStats = await stat(localArtifact).catch(() => null);
|
|
245
|
+
if (!artifactStats?.isFile() || artifactStats.size !== metadata.bytes)
|
|
246
|
+
throw new NativeReleaseRegistryError("Native release artifact size does not match its metadata");
|
|
247
|
+
if (await sha256File(Bun.file(localArtifact)) !== metadata.sha256)
|
|
248
|
+
throw new NativeReleaseRegistryError("Native release artifact digest does not match its metadata");
|
|
249
|
+
const existing = await read({
|
|
250
|
+
appId: metadata.appId,
|
|
251
|
+
platform: metadata.platform,
|
|
252
|
+
releaseId: metadata.releaseId
|
|
253
|
+
});
|
|
254
|
+
let record;
|
|
255
|
+
let reused = false;
|
|
256
|
+
if (existing) {
|
|
257
|
+
if (!sameMetadata(existing.metadata, metadata))
|
|
258
|
+
throw new NativeReleaseRegistryError("Published native release metadata is immutable");
|
|
259
|
+
record = existing;
|
|
260
|
+
reused = true;
|
|
261
|
+
} else {
|
|
262
|
+
const key = artifactKey(metadata);
|
|
263
|
+
const storedArtifact = await options.store.head(key);
|
|
264
|
+
if (storedArtifact) {
|
|
265
|
+
await requireStoredArtifact(key, metadata);
|
|
266
|
+
} else {
|
|
267
|
+
await options.store.put(key, Bun.file(localArtifact).stream(), {
|
|
268
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
269
|
+
contentType: "application/octet-stream",
|
|
270
|
+
maxBytes: metadata.bytes,
|
|
271
|
+
metadata: {
|
|
272
|
+
appId: metadata.appId,
|
|
273
|
+
releaseId: metadata.releaseId,
|
|
274
|
+
sha256: metadata.sha256
|
|
275
|
+
},
|
|
276
|
+
signal: input.signal
|
|
277
|
+
});
|
|
278
|
+
await requireStoredArtifact(key, metadata);
|
|
279
|
+
}
|
|
280
|
+
record = {
|
|
281
|
+
artifactKey: key,
|
|
282
|
+
format: NATIVE_RELEASE_REGISTRY_FORMAT,
|
|
283
|
+
metadata
|
|
284
|
+
};
|
|
285
|
+
const serialized = encodedJson(record);
|
|
286
|
+
await options.store.put(recordKey(metadata), serialized, {
|
|
287
|
+
cacheControl: "public, max-age=31536000, immutable",
|
|
288
|
+
contentType: "application/json",
|
|
289
|
+
maxBytes: serialized.byteLength,
|
|
290
|
+
metadata: {
|
|
291
|
+
releaseId: metadata.releaseId,
|
|
292
|
+
sha256: sha256Bytes(serialized)
|
|
293
|
+
},
|
|
294
|
+
signal: input.signal
|
|
295
|
+
});
|
|
296
|
+
const verified = await read({
|
|
297
|
+
appId: metadata.appId,
|
|
298
|
+
platform: metadata.platform,
|
|
299
|
+
releaseId: metadata.releaseId
|
|
300
|
+
});
|
|
301
|
+
if (!verified || JSON.stringify(verified) !== JSON.stringify(record))
|
|
302
|
+
throw new NativeReleaseRegistryError("Native release publication verification failed");
|
|
303
|
+
}
|
|
304
|
+
const channel = input.channel ? await promote({
|
|
305
|
+
allowUnsigned: input.allowUnsigned,
|
|
306
|
+
appId: metadata.appId,
|
|
307
|
+
channel: input.channel,
|
|
308
|
+
platform: metadata.platform,
|
|
309
|
+
releaseId: metadata.releaseId,
|
|
310
|
+
signal: input.signal
|
|
311
|
+
}) : undefined;
|
|
312
|
+
return { ...channel ? { channel } : {}, record, reused };
|
|
313
|
+
},
|
|
314
|
+
read,
|
|
315
|
+
resolve: async (input) => {
|
|
316
|
+
if (!APP_ID_PATTERN.test(input.appId))
|
|
317
|
+
throw new NativeReleaseRegistryError("Native release appId is invalid");
|
|
318
|
+
const key = channelKey(input.appId, input.platform, input.channel);
|
|
319
|
+
const bytes = await options.store.get(key);
|
|
320
|
+
if (!bytes)
|
|
321
|
+
return null;
|
|
322
|
+
const channel = parseChannel(decodedJson(bytes));
|
|
323
|
+
if (channel.appId !== input.appId || channel.platform !== input.platform || channel.channel !== input.channel)
|
|
324
|
+
throw new NativeReleaseRegistryError("Stored native release channel identity does not match");
|
|
325
|
+
const record = await read({
|
|
326
|
+
appId: channel.appId,
|
|
327
|
+
platform: channel.platform,
|
|
328
|
+
releaseId: channel.releaseId
|
|
329
|
+
});
|
|
330
|
+
if (!record || record.metadata.sha256 !== channel.sha256)
|
|
331
|
+
throw new NativeReleaseRegistryError("Native release channel points to a missing or invalid release");
|
|
332
|
+
return { channel, record };
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
export {
|
|
337
|
+
createNativeReleaseRegistry,
|
|
338
|
+
NativeReleaseRegistryError,
|
|
339
|
+
NATIVE_RELEASE_REGISTRY_FORMAT
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
//# debugId=1FEEE1047DE9DB9664756E2164756E21
|
|
343
|
+
//# sourceMappingURL=nativeRelease.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/nativeRelease.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { createHash } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst DEFAULT_PREFIX = \"absolutejs/native-releases\";\nconst DEFAULT_MAX_ARTIFACT_BYTES = 2_147_483_648;\nexport const NATIVE_RELEASE_REGISTRY_FORMAT = 1 as const;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/;\nconst APP_ID_PATTERN = /^[A-Za-z][\\w]*(?:\\.[A-Za-z][\\w]*)+$/;\nconst CHANNEL_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;\n\nexport type AndroidNativeReleaseMetadata = {\n appBuild: string;\n appId: string;\n artifact: \"app-release.aab\";\n bytes: number;\n engine: \"capacitor\";\n format: 1;\n platform: \"android\";\n releaseId: string;\n runtime: string;\n sha256: string;\n signed: boolean;\n type: \"aab\";\n};\n\nexport type NativeReleaseMetadata = AndroidNativeReleaseMetadata;\n\nexport type NativeReleaseRecord = {\n artifactKey: string;\n format: typeof NATIVE_RELEASE_REGISTRY_FORMAT;\n metadata: NativeReleaseMetadata;\n};\n\nexport type NativeReleaseChannel = {\n appId: string;\n channel: string;\n format: typeof NATIVE_RELEASE_REGISTRY_FORMAT;\n platform: NativeReleaseMetadata[\"platform\"];\n promotedAt: string;\n releaseId: string;\n sha256: string;\n};\n\nexport type NativeReleaseBlobObject = {\n key: string;\n metadata?: Record<string, string>;\n size: number;\n};\n\nexport type NativeReleaseBlobStore = {\n get: (key: string) => Promise<Uint8Array | null>;\n head: (key: string) => Promise<NativeReleaseBlobObject | null>;\n put: (\n key: string,\n body: ReadableStream<Uint8Array> | Uint8Array | string,\n options?: {\n cacheControl?: string;\n contentType?: string;\n maxBytes?: number;\n metadata?: Record<string, string>;\n signal?: AbortSignal;\n },\n ) => Promise<unknown>;\n};\n\nexport type NativeReleasePublication = {\n channel?: NativeReleaseChannel;\n record: NativeReleaseRecord;\n reused: boolean;\n};\n\nexport type NativeReleaseRegistry = {\n promote: (options: {\n allowUnsigned?: boolean;\n appId: string;\n channel: string;\n platform: NativeReleaseMetadata[\"platform\"];\n releaseId: string;\n signal?: AbortSignal;\n }) => Promise<NativeReleaseChannel>;\n publish: (options: {\n allowUnsigned?: boolean;\n channel?: string;\n releaseRoot: string;\n signal?: AbortSignal;\n }) => Promise<NativeReleasePublication>;\n read: (options: {\n appId: string;\n platform: NativeReleaseMetadata[\"platform\"];\n releaseId: string;\n }) => Promise<NativeReleaseRecord | null>;\n resolve: (options: {\n appId: string;\n channel: string;\n platform: NativeReleaseMetadata[\"platform\"];\n }) => Promise<{\n channel: NativeReleaseChannel;\n record: NativeReleaseRecord;\n } | null>;\n};\n\nexport type NativeReleaseRegistryOptions = {\n clock?: () => Date;\n maxArtifactBytes?: number;\n prefix?: string;\n store: NativeReleaseBlobStore;\n};\n\nexport class NativeReleaseRegistryError extends Error {}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst requireString = (value: unknown, field: string) => {\n if (typeof value !== \"string\" || value.length === 0)\n throw new NativeReleaseRegistryError(`Native release ${field} is invalid`);\n\n return value;\n};\n\nconst parseMetadata = (value: unknown): NativeReleaseMetadata => {\n if (!isRecord(value))\n throw new NativeReleaseRegistryError(\"Native release metadata is invalid\");\n const appId = requireString(value.appId, \"appId\");\n const sha256 = requireString(value.sha256, \"sha256\");\n const releaseId = requireString(value.releaseId, \"releaseId\");\n if (!APP_ID_PATTERN.test(appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n if (!SHA256_PATTERN.test(sha256))\n throw new NativeReleaseRegistryError(\"Native release sha256 is invalid\");\n if (releaseId !== `amobile_android_${sha256}`)\n throw new NativeReleaseRegistryError(\n \"Native release id does not match its artifact digest\",\n );\n if (\n value.artifact !== \"app-release.aab\" ||\n value.engine !== \"capacitor\" ||\n value.format !== 1 ||\n value.platform !== \"android\" ||\n value.type !== \"aab\" ||\n typeof value.signed !== \"boolean\" ||\n !Number.isSafeInteger(value.bytes) ||\n Number(value.bytes) < 1\n )\n throw new NativeReleaseRegistryError(\"Native release metadata is invalid\");\n\n return {\n appBuild: requireString(value.appBuild, \"appBuild\"),\n appId,\n artifact: \"app-release.aab\",\n bytes: Number(value.bytes),\n engine: \"capacitor\",\n format: 1,\n platform: \"android\",\n releaseId,\n runtime: requireString(value.runtime, \"runtime\"),\n sha256,\n signed: value.signed,\n type: \"aab\",\n };\n};\n\nconst normalizedPrefix = (value: string) => {\n const prefix = value.replace(/^\\/+|\\/+$/g, \"\");\n if (\n prefix.length === 0 ||\n prefix.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n )\n throw new NativeReleaseRegistryError(\"Native release prefix is invalid\");\n\n return prefix;\n};\n\nconst requireChannel = (value: string) => {\n if (!CHANNEL_PATTERN.test(value))\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n\n return value;\n};\n\nconst appIdentity = (appId: string) =>\n createHash(\"sha256\").update(appId).digest(\"hex\");\n\nconst sha256Bytes = (bytes: Uint8Array) =>\n createHash(\"sha256\").update(bytes).digest(\"hex\");\n\nconst isIsoTimestamp = (value: unknown): value is string => {\n if (typeof value !== \"string\" || Number.isNaN(Date.parse(value)))\n return false;\n\n return new Date(value).toISOString() === value;\n};\n\nconst sha256File = 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 encodedJson = (value: unknown) =>\n new TextEncoder().encode(`${JSON.stringify(value, null, 2)}\\n`);\n\nconst decodedJson = (bytes: Uint8Array) =>\n JSON.parse(new TextDecoder().decode(bytes)) as unknown;\n\nconst sameMetadata = (\n left: NativeReleaseMetadata,\n right: NativeReleaseMetadata,\n) => JSON.stringify(left) === JSON.stringify(right);\n\nconst parseRecord = (value: unknown): NativeReleaseRecord => {\n if (\n !isRecord(value) ||\n value.format !== NATIVE_RELEASE_REGISTRY_FORMAT ||\n typeof value.artifactKey !== \"string\"\n )\n throw new NativeReleaseRegistryError(\"Native release record is invalid\");\n const metadata = parseMetadata(value.metadata);\n\n return {\n artifactKey: value.artifactKey,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n metadata,\n };\n};\n\nconst parseChannel = (value: unknown): NativeReleaseChannel => {\n if (\n !isRecord(value) ||\n value.format !== NATIVE_RELEASE_REGISTRY_FORMAT ||\n value.platform !== \"android\" ||\n !isIsoTimestamp(value.promotedAt)\n )\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n const appId = requireString(value.appId, \"channel appId\");\n const channel = requireChannel(requireString(value.channel, \"channel\"));\n const sha256 = requireString(value.sha256, \"channel sha256\");\n const releaseId = requireString(value.releaseId, \"channel releaseId\");\n if (!APP_ID_PATTERN.test(appId) || !SHA256_PATTERN.test(sha256))\n throw new NativeReleaseRegistryError(\"Native release channel is invalid\");\n if (releaseId !== `amobile_android_${sha256}`)\n throw new NativeReleaseRegistryError(\n \"Native release channel identity does not match\",\n );\n\n return {\n appId,\n channel,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n platform: \"android\",\n promotedAt: value.promotedAt,\n releaseId,\n sha256,\n };\n};\n\nexport const createNativeReleaseRegistry = (\n options: NativeReleaseRegistryOptions,\n): NativeReleaseRegistry => {\n const prefix = normalizedPrefix(options.prefix ?? DEFAULT_PREFIX);\n const maxArtifactBytes =\n options.maxArtifactBytes ?? DEFAULT_MAX_ARTIFACT_BYTES;\n if (!Number.isSafeInteger(maxArtifactBytes) || maxArtifactBytes < 1)\n throw new NativeReleaseRegistryError(\n \"Native release maxArtifactBytes is invalid\",\n );\n const clock = options.clock ?? (() => new Date());\n const appRoot = (appId: string, platform: \"android\") =>\n `${prefix}/${appIdentity(appId)}/${platform}`;\n const releaseRoot = (metadata: NativeReleaseMetadata) =>\n `${appRoot(metadata.appId, metadata.platform)}/releases/${metadata.releaseId}`;\n const recordKey = (metadata: NativeReleaseMetadata) =>\n `${releaseRoot(metadata)}/release.json`;\n const artifactKey = (metadata: NativeReleaseMetadata) =>\n `${releaseRoot(metadata)}/${metadata.artifact}`;\n const channelKey = (appId: string, platform: \"android\", channel: string) =>\n `${appRoot(appId, platform)}/channels/${requireChannel(channel)}.json`;\n\n const requireStoredArtifact = async (\n key: string,\n metadata: NativeReleaseMetadata,\n ) => {\n const stored = await options.store.head(key);\n if (\n !stored ||\n stored.size !== metadata.bytes ||\n stored.metadata?.sha256 !== metadata.sha256 ||\n stored.metadata?.releaseId !== metadata.releaseId\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release artifact does not match its immutable identity\",\n );\n };\n\n const read = async (input: {\n appId: string;\n platform: \"android\";\n releaseId: string;\n }) => {\n if (!APP_ID_PATTERN.test(input.appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n const digest = input.releaseId.replace(/^amobile_android_/, \"\");\n if (\n !SHA256_PATTERN.test(digest) ||\n input.releaseId !== `amobile_android_${digest}`\n )\n throw new NativeReleaseRegistryError(\"Native release id is invalid\");\n const identity: NativeReleaseMetadata = {\n appBuild: \"lookup\",\n appId: input.appId,\n artifact: \"app-release.aab\",\n bytes: 1,\n engine: \"capacitor\",\n format: 1,\n platform: \"android\",\n releaseId: input.releaseId,\n runtime: \"lookup\",\n sha256: digest,\n signed: true,\n type: \"aab\",\n };\n const key = recordKey(identity);\n const bytes = await options.store.get(key);\n if (!bytes) return null;\n const storedRecord = await options.store.head(key);\n if (\n !storedRecord ||\n storedRecord.size !== bytes.byteLength ||\n storedRecord.metadata?.releaseId !== input.releaseId ||\n storedRecord.metadata?.sha256 !== sha256Bytes(bytes)\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release record does not match its immutable identity\",\n );\n const record = parseRecord(decodedJson(bytes));\n if (\n record.metadata.appId !== input.appId ||\n record.metadata.platform !== input.platform ||\n record.metadata.releaseId !== input.releaseId ||\n record.artifactKey !== artifactKey(record.metadata)\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release record identity does not match\",\n );\n await requireStoredArtifact(record.artifactKey, record.metadata);\n\n return record;\n };\n\n const promote: NativeReleaseRegistry[\"promote\"] = async (input) => {\n input.signal?.throwIfAborted();\n const record = await read(input);\n if (!record)\n throw new NativeReleaseRegistryError(\"Native release was not published\");\n if (!record.metadata.signed && !input.allowUnsigned)\n throw new NativeReleaseRegistryError(\n \"Unsigned native releases cannot be promoted\",\n );\n const key = channelKey(input.appId, input.platform, input.channel);\n const existingBytes = await options.store.get(key);\n if (existingBytes) {\n const existing = parseChannel(decodedJson(existingBytes));\n if (\n existing.appId !== input.appId ||\n existing.platform !== input.platform ||\n existing.channel !== input.channel\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release channel identity does not match\",\n );\n if (existing.releaseId === input.releaseId) return existing;\n }\n const channel: NativeReleaseChannel = {\n appId: input.appId,\n channel: requireChannel(input.channel),\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n platform: input.platform,\n promotedAt: clock().toISOString(),\n releaseId: record.metadata.releaseId,\n sha256: record.metadata.sha256,\n };\n const serialized = encodedJson(channel);\n await options.store.put(key, serialized, {\n cacheControl: \"no-cache\",\n contentType: \"application/json\",\n maxBytes: serialized.byteLength,\n metadata: {\n channel: channel.channel,\n releaseId: channel.releaseId,\n sha256: channel.sha256,\n },\n signal: input.signal,\n });\n const stored = await options.store.get(key);\n if (\n !stored ||\n JSON.stringify(parseChannel(decodedJson(stored))) !==\n JSON.stringify(channel)\n )\n throw new NativeReleaseRegistryError(\n \"Native release channel verification failed\",\n );\n\n return channel;\n };\n\n return {\n promote,\n publish: async (input) => {\n input.signal?.throwIfAborted();\n const localRoot = path.resolve(input.releaseRoot);\n const metadata = parseMetadata(\n JSON.parse(\n await readFile(path.join(localRoot, \"release.json\"), \"utf8\"),\n ),\n );\n if (metadata.bytes > maxArtifactBytes)\n throw new NativeReleaseRegistryError(\n \"Native release exceeds the configured artifact limit\",\n );\n if (!metadata.signed && !input.allowUnsigned)\n throw new NativeReleaseRegistryError(\n \"Unsigned native releases cannot be published\",\n );\n const localArtifact = path.join(localRoot, metadata.artifact);\n const artifactStats = await stat(localArtifact).catch(() => null);\n if (!artifactStats?.isFile() || artifactStats.size !== metadata.bytes)\n throw new NativeReleaseRegistryError(\n \"Native release artifact size does not match its metadata\",\n );\n if ((await sha256File(Bun.file(localArtifact))) !== metadata.sha256)\n throw new NativeReleaseRegistryError(\n \"Native release artifact digest does not match its metadata\",\n );\n const existing = await read({\n appId: metadata.appId,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n });\n let record: NativeReleaseRecord;\n let reused = false;\n if (existing) {\n if (!sameMetadata(existing.metadata, metadata))\n throw new NativeReleaseRegistryError(\n \"Published native release metadata is immutable\",\n );\n record = existing;\n reused = true;\n } else {\n const key = artifactKey(metadata);\n const storedArtifact = await options.store.head(key);\n if (storedArtifact) {\n await requireStoredArtifact(key, metadata);\n } else {\n await options.store.put(key, Bun.file(localArtifact).stream(), {\n cacheControl: \"public, max-age=31536000, immutable\",\n contentType: \"application/octet-stream\",\n maxBytes: metadata.bytes,\n metadata: {\n appId: metadata.appId,\n releaseId: metadata.releaseId,\n sha256: metadata.sha256,\n },\n signal: input.signal,\n });\n await requireStoredArtifact(key, metadata);\n }\n record = {\n artifactKey: key,\n format: NATIVE_RELEASE_REGISTRY_FORMAT,\n metadata,\n };\n const serialized = encodedJson(record);\n await options.store.put(recordKey(metadata), serialized, {\n cacheControl: \"public, max-age=31536000, immutable\",\n contentType: \"application/json\",\n maxBytes: serialized.byteLength,\n metadata: {\n releaseId: metadata.releaseId,\n sha256: sha256Bytes(serialized),\n },\n signal: input.signal,\n });\n const verified = await read({\n appId: metadata.appId,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n });\n if (!verified || JSON.stringify(verified) !== JSON.stringify(record))\n throw new NativeReleaseRegistryError(\n \"Native release publication verification failed\",\n );\n }\n const channel = input.channel\n ? await promote({\n allowUnsigned: input.allowUnsigned,\n appId: metadata.appId,\n channel: input.channel,\n platform: metadata.platform,\n releaseId: metadata.releaseId,\n signal: input.signal,\n })\n : undefined;\n\n return { ...(channel ? { channel } : {}), record, reused };\n },\n read,\n resolve: async (input) => {\n if (!APP_ID_PATTERN.test(input.appId))\n throw new NativeReleaseRegistryError(\"Native release appId is invalid\");\n const key = channelKey(input.appId, input.platform, input.channel);\n const bytes = await options.store.get(key);\n if (!bytes) return null;\n const channel = parseChannel(decodedJson(bytes));\n if (\n channel.appId !== input.appId ||\n channel.platform !== input.platform ||\n channel.channel !== input.channel\n )\n throw new NativeReleaseRegistryError(\n \"Stored native release channel identity does not match\",\n );\n const record = await read({\n appId: channel.appId,\n platform: channel.platform,\n releaseId: channel.releaseId,\n });\n if (!record || record.metadata.sha256 !== channel.sha256)\n throw new NativeReleaseRegistryError(\n \"Native release channel points to a missing or invalid release\",\n );\n\n return { channel, record };\n },\n };\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "1FEEE1047DE9DB9664756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/releaseArtifact.js
CHANGED
|
@@ -129,7 +129,6 @@ var receiveReleaseArtifact = async (options) => {
|
|
|
129
129
|
await rm(options.destination, { force: true });
|
|
130
130
|
throw error;
|
|
131
131
|
} finally {
|
|
132
|
-
reader.releaseLock();
|
|
133
132
|
await writer.end();
|
|
134
133
|
}
|
|
135
134
|
if (bytes !== options.expectedBytes || hasher.digest("hex") !== options.expectedSha256) {
|
|
@@ -171,5 +170,5 @@ export {
|
|
|
171
170
|
ReleaseArtifactError
|
|
172
171
|
};
|
|
173
172
|
|
|
174
|
-
//# debugId=
|
|
173
|
+
//# debugId=8414E4F4B329037664756E2164756E21
|
|
175
174
|
//# sourceMappingURL=releaseArtifact.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/releaseArtifact.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { mkdir, mkdtemp, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\n\nconst DEFAULT_MAX_BYTES = 2_147_483_648;\nconst ERROR_DETAIL_LIMIT = 500;\nconst SAFE_RELEASE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;\n\nexport type ReleaseArtifactMetadata = {\n bytes: number;\n releaseId: string;\n sha256: string;\n};\n\nexport type CreatedReleaseArtifact = {\n dispose: () => Promise<void>;\n file: Bun.BunFile;\n metadata: ReleaseArtifactMetadata;\n path: string;\n};\n\nexport class ReleaseArtifactError extends Error {}\n\nconst run = async (command: string[]) => {\n const process = Bun.spawn(command, { stderr: \"pipe\", stdout: \"pipe\" });\n const [exitCode, stderr, stdout] = await Promise.all([\n process.exited,\n new Response(process.stderr).text(),\n new Response(process.stdout).text(),\n ]);\n if (exitCode !== 0)\n throw new ReleaseArtifactError(\n `${command[0]} failed (${exitCode}): ${stderr.slice(0, ERROR_DETAIL_LIMIT)}`,\n );\n\n return stdout;\n};\n\nconst assertReleaseId = (value: string) => {\n if (!SAFE_RELEASE_ID.test(value))\n throw new ReleaseArtifactError(\"Release id is invalid\");\n\n return value;\n};\n\nconst assertExclude = (value: string) => {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.startsWith(\"/\") ||\n value.split(\"/\").some((part) => part === \"..\") ||\n /[\\0\\r\\n]/.test(value)\n )\n throw new ReleaseArtifactError(`Invalid release exclusion: ${value}`);\n\n return value.replace(/^\\.\\//, \"\");\n};\n\nconst sha256File = async (file: Blob) => {\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\nexport const createReleaseArtifact = async (options: {\n exclude?: string[];\n releaseId?: string;\n sourceRoot: string;\n temporaryRoot?: string;\n}): Promise<CreatedReleaseArtifact> => {\n const releaseId = assertReleaseId(options.releaseId ?? crypto.randomUUID());\n const source = path.resolve(options.sourceRoot);\n const sourceStats = await stat(source).catch(() => null);\n if (!sourceStats?.isDirectory())\n throw new ReleaseArtifactError(\"Release source root is not a directory\");\n if (!(await Bun.file(path.join(source, \"package.json\")).exists()))\n throw new ReleaseArtifactError(\"Release source has no package.json\");\n const temporary = await mkdtemp(\n path.join(options.temporaryRoot ?? tmpdir(), \"absolutejs-release-\"),\n );\n const archivePath = path.join(temporary, `${releaseId}.tgz`);\n try {\n await run([\n \"tar\",\n \"-czf\",\n archivePath,\n ...(options.exclude ?? []).map(\n (value) => `--exclude=./${assertExclude(value)}`,\n ),\n \"-C\",\n source,\n \".\",\n ]);\n const file = Bun.file(archivePath);\n\n return {\n dispose: () => rm(temporary, { force: true, recursive: true }),\n file,\n metadata: {\n bytes: file.size,\n releaseId,\n sha256: await sha256File(file),\n },\n path: archivePath,\n };\n } catch (error) {\n await rm(temporary, { force: true, recursive: true });\n throw error;\n }\n};\n\nexport const receiveReleaseArtifact = async (options: {\n destination: string;\n expectedBytes: number;\n expectedSha256: string;\n maxBytes?: number;\n stream: ReadableStream<Uint8Array>;\n}) => {\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n if (\n !Number.isSafeInteger(options.expectedBytes) ||\n options.expectedBytes < 1 ||\n options.expectedBytes > maxBytes ||\n !/^[a-f0-9]{64}$/.test(options.expectedSha256)\n )\n throw new ReleaseArtifactError(\"Release artifact metadata is invalid\");\n await mkdir(path.dirname(options.destination), { recursive: true });\n const writer = Bun.file(options.destination).writer();\n const hasher = new Bun.CryptoHasher(\"sha256\");\n const reader = options.stream.getReader();\n let bytes = 0;\n try {\n while (true) {\n const { done, value: chunk } = await reader.read();\n if (done) break;\n bytes += chunk.byteLength;\n if (bytes > options.expectedBytes || bytes > maxBytes)\n throw new ReleaseArtifactError(\n \"Release artifact exceeds its declared size\",\n );\n hasher.update(chunk);\n writer.write(chunk);\n }\n } catch (error) {\n await rm(options.destination, { force: true });\n throw error;\n } finally {\n
|
|
5
|
+
"import { mkdir, mkdtemp, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\n\nconst DEFAULT_MAX_BYTES = 2_147_483_648;\nconst ERROR_DETAIL_LIMIT = 500;\nconst SAFE_RELEASE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;\n\nexport type ReleaseArtifactMetadata = {\n bytes: number;\n releaseId: string;\n sha256: string;\n};\n\nexport type CreatedReleaseArtifact = {\n dispose: () => Promise<void>;\n file: Bun.BunFile;\n metadata: ReleaseArtifactMetadata;\n path: string;\n};\n\nexport class ReleaseArtifactError extends Error {}\n\nconst run = async (command: string[]) => {\n const process = Bun.spawn(command, { stderr: \"pipe\", stdout: \"pipe\" });\n const [exitCode, stderr, stdout] = await Promise.all([\n process.exited,\n new Response(process.stderr).text(),\n new Response(process.stdout).text(),\n ]);\n if (exitCode !== 0)\n throw new ReleaseArtifactError(\n `${command[0]} failed (${exitCode}): ${stderr.slice(0, ERROR_DETAIL_LIMIT)}`,\n );\n\n return stdout;\n};\n\nconst assertReleaseId = (value: string) => {\n if (!SAFE_RELEASE_ID.test(value))\n throw new ReleaseArtifactError(\"Release id is invalid\");\n\n return value;\n};\n\nconst assertExclude = (value: string) => {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.startsWith(\"/\") ||\n value.split(\"/\").some((part) => part === \"..\") ||\n /[\\0\\r\\n]/.test(value)\n )\n throw new ReleaseArtifactError(`Invalid release exclusion: ${value}`);\n\n return value.replace(/^\\.\\//, \"\");\n};\n\nconst sha256File = async (file: Blob) => {\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\nexport const createReleaseArtifact = async (options: {\n exclude?: string[];\n releaseId?: string;\n sourceRoot: string;\n temporaryRoot?: string;\n}): Promise<CreatedReleaseArtifact> => {\n const releaseId = assertReleaseId(options.releaseId ?? crypto.randomUUID());\n const source = path.resolve(options.sourceRoot);\n const sourceStats = await stat(source).catch(() => null);\n if (!sourceStats?.isDirectory())\n throw new ReleaseArtifactError(\"Release source root is not a directory\");\n if (!(await Bun.file(path.join(source, \"package.json\")).exists()))\n throw new ReleaseArtifactError(\"Release source has no package.json\");\n const temporary = await mkdtemp(\n path.join(options.temporaryRoot ?? tmpdir(), \"absolutejs-release-\"),\n );\n const archivePath = path.join(temporary, `${releaseId}.tgz`);\n try {\n await run([\n \"tar\",\n \"-czf\",\n archivePath,\n ...(options.exclude ?? []).map(\n (value) => `--exclude=./${assertExclude(value)}`,\n ),\n \"-C\",\n source,\n \".\",\n ]);\n const file = Bun.file(archivePath);\n\n return {\n dispose: () => rm(temporary, { force: true, recursive: true }),\n file,\n metadata: {\n bytes: file.size,\n releaseId,\n sha256: await sha256File(file),\n },\n path: archivePath,\n };\n } catch (error) {\n await rm(temporary, { force: true, recursive: true });\n throw error;\n }\n};\n\nexport const receiveReleaseArtifact = async (options: {\n destination: string;\n expectedBytes: number;\n expectedSha256: string;\n maxBytes?: number;\n stream: ReadableStream<Uint8Array>;\n}) => {\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n if (\n !Number.isSafeInteger(options.expectedBytes) ||\n options.expectedBytes < 1 ||\n options.expectedBytes > maxBytes ||\n !/^[a-f0-9]{64}$/.test(options.expectedSha256)\n )\n throw new ReleaseArtifactError(\"Release artifact metadata is invalid\");\n await mkdir(path.dirname(options.destination), { recursive: true });\n const writer = Bun.file(options.destination).writer();\n const hasher = new Bun.CryptoHasher(\"sha256\");\n const reader = options.stream.getReader();\n let bytes = 0;\n try {\n while (true) {\n const { done, value: chunk } = await reader.read();\n if (done) break;\n bytes += chunk.byteLength;\n if (bytes > options.expectedBytes || bytes > maxBytes)\n throw new ReleaseArtifactError(\n \"Release artifact exceeds its declared size\",\n );\n hasher.update(chunk);\n writer.write(chunk);\n }\n } catch (error) {\n await rm(options.destination, { force: true });\n throw error;\n } finally {\n await writer.end();\n }\n if (\n bytes !== options.expectedBytes ||\n hasher.digest(\"hex\") !== options.expectedSha256\n ) {\n await rm(options.destination, { force: true });\n throw new ReleaseArtifactError(\n \"Release artifact integrity verification failed\",\n );\n }\n\n return { bytes, sha256: options.expectedSha256 };\n};\n\nexport const extractReleaseArtifact = async (options: {\n archivePath: string;\n destination: string;\n}) => {\n const [names, verbose] = await Promise.all([\n run([\"tar\", \"-tzf\", options.archivePath]),\n run([\"tar\", \"-tvzf\", options.archivePath]),\n ]);\n const unsafePath = names\n .split(\"\\n\")\n .filter(Boolean)\n .some(\n (entry) =>\n entry.startsWith(\"/\") || entry.split(\"/\").some((part) => part === \"..\"),\n );\n const unsafeType = verbose\n .split(\"\\n\")\n .filter(Boolean)\n .some((entry) => entry[0] !== \"-\" && entry[0] !== \"d\");\n if (unsafePath || unsafeType)\n throw new ReleaseArtifactError(\"Release artifact contains an unsafe entry\");\n await rm(options.destination, { force: true, recursive: true });\n await mkdir(options.destination, { recursive: true });\n await run([\n \"tar\",\n \"-xzf\",\n options.archivePath,\n \"--no-same-owner\",\n \"--no-same-permissions\",\n \"-C\",\n options.destination,\n ]);\n if (\n !(await Bun.file(path.join(options.destination, \"package.json\")).exists())\n )\n throw new ReleaseArtifactError(\"Release artifact has no package.json\");\n\n return { extracted: true } as const;\n};\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "8414E4F4B329037664756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/deploy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Generic Bun-project deploy pipeline. A Target (localTarget / sshTarget) is anywhere you can exec + upload — DigitalOcean droplets, Linode, Hetzner, Vultr, your own boxes. Bundled pipeline: prepare → upload → install → build → link → restart → verify. Atomic symlink swap, release history, prune, hooks. SSH shells out to system ssh/rsync — zero ssh2 deps.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"release"
|
|
31
31
|
],
|
|
32
32
|
"scripts": {
|
|
33
|
-
"build": "rm -rf dist && bun build src/index.ts src/releaseArtifact.ts src/infrastructure.ts src/ephemeralInfrastructure.ts src/edgeIngress.ts src/digitalocean.ts src/digitaloceanInfrastructure.ts src/digitaloceanEphemeralInfrastructure.ts src/digitaloceanIngress.ts src/gcp.ts src/gcpIngress.ts src/hetzner.ts src/hetznerInfrastructure.ts src/linode.ts src/linodeInfrastructure.ts src/vultr.ts src/vultrInfrastructure.ts src/dns.ts src/cloudflare.ts src/digitaloceanDns.ts src/hetznerDns.ts src/route53.ts src/tls.ts src/env.ts src/preview.ts src/managedPreview.ts --outdir dist --root src --sourcemap --target=bun && tsc --project tsconfig.build.json",
|
|
33
|
+
"build": "rm -rf dist && bun build src/index.ts src/releaseArtifact.ts src/nativeRelease.ts src/infrastructure.ts src/ephemeralInfrastructure.ts src/edgeIngress.ts src/digitalocean.ts src/digitaloceanInfrastructure.ts src/digitaloceanEphemeralInfrastructure.ts src/digitaloceanIngress.ts src/gcp.ts src/gcpIngress.ts src/hetzner.ts src/hetznerInfrastructure.ts src/linode.ts src/linodeInfrastructure.ts src/vultr.ts src/vultrInfrastructure.ts src/dns.ts src/cloudflare.ts src/digitaloceanDns.ts src/hetznerDns.ts src/route53.ts src/tls.ts src/env.ts src/preview.ts src/managedPreview.ts --outdir dist --root src --sourcemap --target=bun && tsc --project tsconfig.build.json",
|
|
34
34
|
"test": "bun test tests/",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
36
|
"format": "prettier --write \"./**/*.{ts,json,md}\"",
|
|
@@ -86,6 +86,11 @@
|
|
|
86
86
|
"import": "./dist/releaseArtifact.js",
|
|
87
87
|
"default": "./dist/releaseArtifact.js"
|
|
88
88
|
},
|
|
89
|
+
"./native-release": {
|
|
90
|
+
"types": "./dist/nativeRelease.d.ts",
|
|
91
|
+
"import": "./dist/nativeRelease.js",
|
|
92
|
+
"default": "./dist/nativeRelease.js"
|
|
93
|
+
},
|
|
89
94
|
"./digitalocean-ingress": {
|
|
90
95
|
"types": "./dist/digitaloceanIngress.d.ts",
|
|
91
96
|
"import": "./dist/digitaloceanIngress.js",
|