@hot-updater/aws 1.0.0-rc.1 → 1.0.0-rc.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/iac/index.cjs +569 -1979
- package/dist/iac/index.d.cts +102 -8
- package/dist/iac/index.d.mts +103 -9
- package/dist/iac/index.mjs +560 -1977
- package/dist/index.cjs +270 -1723
- package/dist/index.d.cts +351 -9
- package/dist/index.d.mts +351 -10
- package/dist/index.mjs +270 -1724
- package/dist/lambda/index.cjs +7838 -6253
- package/dist/lambda/index.d.cts +344 -2
- package/package.json +7 -8
package/dist/iac/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
9
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
10
10
|
var __copyProps = (to, from, except, desc) => {
|
|
11
11
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
12
|
key = keys[i];
|
|
@@ -17,25 +17,25 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
}
|
|
18
18
|
return to;
|
|
19
19
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
21
21
|
value: mod,
|
|
22
22
|
enumerable: true
|
|
23
23
|
}) : target, mod));
|
|
24
24
|
//#endregion
|
|
25
25
|
let _hot_updater_cli_tools = require("@hot-updater/cli-tools");
|
|
26
|
-
let
|
|
26
|
+
let _hot_updater_server_db = require("@hot-updater/server/db");
|
|
27
27
|
let node_url = require("node:url");
|
|
28
28
|
let node_child_process = require("node:child_process");
|
|
29
29
|
let node_string_decoder = require("node:string_decoder");
|
|
30
30
|
let node_util = require("node:util");
|
|
31
31
|
let node_process = require("node:process");
|
|
32
|
-
node_process = __toESM(node_process);
|
|
32
|
+
node_process = __toESM(node_process, 1);
|
|
33
33
|
let node_tty = require("node:tty");
|
|
34
|
-
node_tty = __toESM(node_tty);
|
|
34
|
+
node_tty = __toESM(node_tty, 1);
|
|
35
35
|
let node_path = require("node:path");
|
|
36
|
-
node_path = __toESM(node_path);
|
|
36
|
+
node_path = __toESM(node_path, 1);
|
|
37
37
|
let path = require("path");
|
|
38
|
-
path = __toESM(path);
|
|
38
|
+
path = __toESM(path, 1);
|
|
39
39
|
let node_timers_promises = require("node:timers/promises");
|
|
40
40
|
let node_os = require("node:os");
|
|
41
41
|
let node_events = require("node:events");
|
|
@@ -44,20 +44,21 @@ let node_fs = require("node:fs");
|
|
|
44
44
|
let node_stream_promises = require("node:stream/promises");
|
|
45
45
|
let node_stream = require("node:stream");
|
|
46
46
|
let node_buffer = require("node:buffer");
|
|
47
|
-
let _aws_sdk_client_cloudfront = require("@aws-sdk/client-cloudfront");
|
|
48
47
|
let _aws_sdk_client_dynamodb = require("@aws-sdk/client-dynamodb");
|
|
48
|
+
let _hot_updater_server_database = require("@hot-updater/server/database");
|
|
49
|
+
let _aws_sdk_client_cloudfront = require("@aws-sdk/client-cloudfront");
|
|
49
50
|
let _aws_sdk_lib_dynamodb = require("@aws-sdk/lib-dynamodb");
|
|
50
|
-
let
|
|
51
|
-
let
|
|
51
|
+
let _hot_updater_server_plugins_api_keys = require("@hot-updater/server/plugins/api-keys");
|
|
52
|
+
let _hot_updater_server_plugins_insights = require("@hot-updater/server/plugins/insights");
|
|
52
53
|
let _aws_sdk_credential_providers = require("@aws-sdk/credential-providers");
|
|
53
54
|
let _aws_sdk_client_lambda = require("@aws-sdk/client-lambda");
|
|
54
|
-
let crypto = require("crypto");
|
|
55
|
-
crypto = __toESM(crypto);
|
|
55
|
+
let crypto$1 = require("crypto");
|
|
56
|
+
crypto$1 = __toESM(crypto$1, 1);
|
|
56
57
|
let node_crypto = require("node:crypto");
|
|
57
58
|
let _aws_sdk_client_iam = require("@aws-sdk/client-iam");
|
|
58
59
|
let _aws_sdk_client_sts = require("@aws-sdk/client-sts");
|
|
59
60
|
let fs_promises = require("fs/promises");
|
|
60
|
-
fs_promises = __toESM(fs_promises);
|
|
61
|
+
fs_promises = __toESM(fs_promises, 1);
|
|
61
62
|
let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
62
63
|
let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
|
|
63
64
|
//#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
@@ -158,7 +159,8 @@ const parseTemplate = ({ templates, expressions, tokens, index, template }) => {
|
|
|
158
159
|
const newTokens = concatTokens(tokens, nextTokens, leadingWhitespaces);
|
|
159
160
|
if (index === expressions.length) return newTokens;
|
|
160
161
|
const expression = expressions[index];
|
|
161
|
-
|
|
162
|
+
const expressionTokens = Array.isArray(expression) ? expression.map((expression) => parseExpression(expression)) : [parseExpression(expression)];
|
|
163
|
+
return concatTokens(newTokens, expressionTokens, trailingWhitespaces);
|
|
162
164
|
};
|
|
163
165
|
const splitByWhitespaces = (template, rawTemplate) => {
|
|
164
166
|
if (rawTemplate.length === 0) return {
|
|
@@ -191,7 +193,7 @@ const splitByWhitespaces = (template, rawTemplate) => {
|
|
|
191
193
|
trailingWhitespaces
|
|
192
194
|
};
|
|
193
195
|
};
|
|
194
|
-
const DELIMITERS = new Set([
|
|
196
|
+
const DELIMITERS = /* @__PURE__ */ new Set([
|
|
195
197
|
" ",
|
|
196
198
|
" ",
|
|
197
199
|
"\r",
|
|
@@ -243,7 +245,8 @@ const normalizeFdSpecificOptions = (options) => {
|
|
|
243
245
|
};
|
|
244
246
|
const normalizeFdSpecificOption = (options, optionName) => {
|
|
245
247
|
const optionBaseArray = Array.from({ length: getStdioLength(options) + 1 });
|
|
246
|
-
|
|
248
|
+
const optionArray = normalizeFdSpecificValue(options[optionName], optionBaseArray, optionName);
|
|
249
|
+
return addDefaultValue$1(optionArray, optionName);
|
|
247
250
|
};
|
|
248
251
|
const getStdioLength = ({ stdio }) => Array.isArray(stdio) ? Math.max(stdio.length, STANDARD_STREAMS_ALIASES.length) : STANDARD_STREAMS_ALIASES.length;
|
|
249
252
|
const normalizeFdSpecificValue = (optionValue, optionArray, optionName) => isPlainObject(optionValue) ? normalizeOptionObject(optionValue, optionArray, optionName) : optionArray.fill(optionValue);
|
|
@@ -276,7 +279,7 @@ const addDefaultValue$1 = (optionArray, optionName) => optionArray.map((optionVa
|
|
|
276
279
|
const DEFAULT_OPTIONS = {
|
|
277
280
|
lines: false,
|
|
278
281
|
buffer: true,
|
|
279
|
-
maxBuffer:
|
|
282
|
+
maxBuffer: 1e8,
|
|
280
283
|
verbose: (0, node_util.debuglog)("execa").enabled ? "full" : "none",
|
|
281
284
|
stripFinalNewline: true
|
|
282
285
|
};
|
|
@@ -324,7 +327,7 @@ const escapeControlCharacter = (character) => {
|
|
|
324
327
|
};
|
|
325
328
|
const getSpecialCharRegExp = () => {
|
|
326
329
|
try {
|
|
327
|
-
return
|
|
330
|
+
return new RegExp("\\p{Separator}|\\p{Other}", "gu");
|
|
328
331
|
} catch {
|
|
329
332
|
return /[\s\u0000-\u001F\u007F-\u009F\u00AD]/g;
|
|
330
333
|
}
|
|
@@ -744,11 +747,13 @@ const appendNewline = (printedLine) => printedLine.endsWith("\n") ? printedLine
|
|
|
744
747
|
//#endregion
|
|
745
748
|
//#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/verbose/log.js
|
|
746
749
|
const verboseLog = ({ type, verboseMessage, fdNumber, verboseInfo, result }) => {
|
|
747
|
-
const
|
|
750
|
+
const verboseObject = getVerboseObject({
|
|
748
751
|
type,
|
|
749
752
|
result,
|
|
750
753
|
verboseInfo
|
|
751
|
-
})
|
|
754
|
+
});
|
|
755
|
+
const printedLines = getPrintedLines(verboseMessage, verboseObject);
|
|
756
|
+
const finalLines = applyVerboseOnLines(printedLines, verboseInfo, fdNumber);
|
|
752
757
|
if (finalLines !== "") console.warn(finalLines.slice(0, -1));
|
|
753
758
|
};
|
|
754
759
|
const getVerboseObject = ({ type, result, verboseInfo: { escapedCommand, commandId, rawOptions: { piped = false, ...options } } }) => ({
|
|
@@ -771,7 +776,8 @@ const getPrintedLine = (verboseObject) => {
|
|
|
771
776
|
};
|
|
772
777
|
};
|
|
773
778
|
const serializeVerboseMessage = (message) => {
|
|
774
|
-
|
|
779
|
+
const messageString = typeof message === "string" ? message : (0, node_util.inspect)(message);
|
|
780
|
+
return escapeLines(messageString).replaceAll(" ", " ".repeat(TAB_SIZE));
|
|
775
781
|
};
|
|
776
782
|
const TAB_SIZE = 2;
|
|
777
783
|
//#endregion
|
|
@@ -816,7 +822,8 @@ const getDurationMs = (startTime) => Number(node_process.hrtime.bigint() - start
|
|
|
816
822
|
const handleCommand = (filePath, rawArguments, rawOptions) => {
|
|
817
823
|
const startTime = getStartTime();
|
|
818
824
|
const { command, escapedCommand } = joinCommand(filePath, rawArguments);
|
|
819
|
-
const
|
|
825
|
+
const verbose = normalizeFdSpecificOption(rawOptions, "verbose");
|
|
826
|
+
const verboseInfo = getVerboseInfo(verbose, escapedCommand, { ...rawOptions });
|
|
820
827
|
logCommand(escapedCommand, verboseInfo);
|
|
821
828
|
return {
|
|
822
829
|
command,
|
|
@@ -937,7 +944,8 @@ var require_which = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
937
944
|
const getNotFoundError = (cmd) => Object.assign(/* @__PURE__ */ new Error(`not found: ${cmd}`), { code: "ENOENT" });
|
|
938
945
|
const getPathInfo = (cmd, opt) => {
|
|
939
946
|
const colon = opt.colon || COLON;
|
|
940
|
-
const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [""] : [...isWindows ? [process.cwd()] : [], ...(opt.path || process.env.PATH ||
|
|
947
|
+
const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [""] : [...isWindows ? [process.cwd()] : [], ...(opt.path || process.env.PATH ||
|
|
948
|
+
/* istanbul ignore next: very unusual */ "").split(colon)];
|
|
941
949
|
const pathExtExe = isWindows ? opt.pathExt || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM" : "";
|
|
942
950
|
const pathExt = isWindows ? pathExtExe.split(colon) : [""];
|
|
943
951
|
if (isWindows) {
|
|
@@ -962,14 +970,17 @@ var require_which = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
962
970
|
const ppRaw = pathEnv[i];
|
|
963
971
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
964
972
|
const pCmd = path$9.join(pathPart, cmd);
|
|
965
|
-
|
|
973
|
+
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
974
|
+
resolve(subStep(p, i, 0));
|
|
966
975
|
});
|
|
967
976
|
const subStep = (p, i, ii) => new Promise((resolve, reject) => {
|
|
968
977
|
if (ii === pathExt.length) return resolve(step(i + 1));
|
|
969
978
|
const ext = pathExt[ii];
|
|
970
979
|
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
971
|
-
if (!er && is)
|
|
972
|
-
|
|
980
|
+
if (!er && is) {
|
|
981
|
+
if (opt.all) found.push(p + ext);
|
|
982
|
+
else return resolve(p + ext);
|
|
983
|
+
}
|
|
973
984
|
return resolve(subStep(p, i, ii + 1));
|
|
974
985
|
});
|
|
975
986
|
});
|
|
@@ -987,8 +998,10 @@ var require_which = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
987
998
|
for (let j = 0; j < pathExt.length; j++) {
|
|
988
999
|
const cur = p + pathExt[j];
|
|
989
1000
|
try {
|
|
990
|
-
if (isexe.sync(cur, { pathExt: pathExtExe }))
|
|
991
|
-
|
|
1001
|
+
if (isexe.sync(cur, { pathExt: pathExtExe })) {
|
|
1002
|
+
if (opt.all) found.push(cur);
|
|
1003
|
+
else return cur;
|
|
1004
|
+
}
|
|
992
1005
|
} catch (ex) {}
|
|
993
1006
|
}
|
|
994
1007
|
}
|
|
@@ -1679,7 +1692,7 @@ const normalizeForceKillAfterDelay = (forceKillAfterDelay) => {
|
|
|
1679
1692
|
if (!Number.isFinite(forceKillAfterDelay) || forceKillAfterDelay < 0) throw new TypeError(`Expected the \`forceKillAfterDelay\` option to be a non-negative integer, got \`${forceKillAfterDelay}\` (${typeof forceKillAfterDelay})`);
|
|
1680
1693
|
return forceKillAfterDelay;
|
|
1681
1694
|
};
|
|
1682
|
-
const DEFAULT_FORCE_KILL_TIMEOUT =
|
|
1695
|
+
const DEFAULT_FORCE_KILL_TIMEOUT = 5e3;
|
|
1683
1696
|
const subprocessKill = ({ kill, options: { forceKillAfterDelay, killSignal }, onInternalError, context, controller }, signalOrError, errorArgument) => {
|
|
1684
1697
|
const { signal, error } = parseKillArguments(signalOrError, errorArgument, killSignal);
|
|
1685
1698
|
emitKillError(error, onInternalError);
|
|
@@ -1781,7 +1794,7 @@ const handleSerializationError = ({ error, methodName, isSubprocess, message })
|
|
|
1781
1794
|
if (isSerializationError(error)) throw new Error(`${getMethodName(methodName, isSubprocess)}'s argument type is invalid: the message cannot be serialized: ${String(message)}.`, { cause: error });
|
|
1782
1795
|
};
|
|
1783
1796
|
const isSerializationError = ({ code, message }) => SERIALIZATION_ERROR_CODES.has(code) || SERIALIZATION_ERROR_MESSAGES.some((serializationErrorMessage) => message.includes(serializationErrorMessage));
|
|
1784
|
-
const SERIALIZATION_ERROR_CODES = new Set(["ERR_MISSING_ARGS", "ERR_INVALID_ARG_TYPE"]);
|
|
1797
|
+
const SERIALIZATION_ERROR_CODES = /* @__PURE__ */ new Set(["ERR_MISSING_ARGS", "ERR_INVALID_ARG_TYPE"]);
|
|
1785
1798
|
const SERIALIZATION_ERROR_MESSAGES = [
|
|
1786
1799
|
"could not be cloned",
|
|
1787
1800
|
"circular structure",
|
|
@@ -1991,7 +2004,8 @@ const isConnected = (anyProcess) => {
|
|
|
1991
2004
|
//#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/ipc/strict.js
|
|
1992
2005
|
const handleSendStrict = ({ anyProcess, channel, isSubprocess, message, strict }) => {
|
|
1993
2006
|
if (!strict) return message;
|
|
1994
|
-
const
|
|
2007
|
+
const ipcEmitter = getIpcEmitter(anyProcess, channel, isSubprocess);
|
|
2008
|
+
const hasListeners = hasMessageListeners(anyProcess, ipcEmitter);
|
|
1995
2009
|
return {
|
|
1996
2010
|
id: count++,
|
|
1997
2011
|
type: REQUEST_TYPE,
|
|
@@ -2223,7 +2237,8 @@ const throwOnGracefulCancel = ({ subprocess, cancelSignal, gracefulCancel, force
|
|
|
2223
2237
|
})] : [];
|
|
2224
2238
|
const sendOnAbort = async ({ subprocess, cancelSignal, forceKillAfterDelay, context, controller: { signal } }) => {
|
|
2225
2239
|
await onAbortedSignal(cancelSignal, signal);
|
|
2226
|
-
|
|
2240
|
+
const reason = getReason(cancelSignal);
|
|
2241
|
+
await sendAbort(subprocess, reason);
|
|
2227
2242
|
killOnTimeout({
|
|
2228
2243
|
kill: subprocess.kill,
|
|
2229
2244
|
forceKillAfterDelay,
|
|
@@ -2335,8 +2350,8 @@ Please rename it to ${serializeEncoding(correctEncoding)}.`);
|
|
|
2335
2350
|
throw new TypeError(`Invalid option \`encoding: ${serializeEncoding(encoding)}\`.
|
|
2336
2351
|
Please rename it to one of: ${correctEncodings}.`);
|
|
2337
2352
|
};
|
|
2338
|
-
const TEXT_ENCODINGS = new Set(["utf8", "utf16le"]);
|
|
2339
|
-
const BINARY_ENCODINGS = new Set([
|
|
2353
|
+
const TEXT_ENCODINGS = /* @__PURE__ */ new Set(["utf8", "utf16le"]);
|
|
2354
|
+
const BINARY_ENCODINGS = /* @__PURE__ */ new Set([
|
|
2340
2355
|
"buffer",
|
|
2341
2356
|
"hex",
|
|
2342
2357
|
"base64",
|
|
@@ -2344,7 +2359,7 @@ const BINARY_ENCODINGS = new Set([
|
|
|
2344
2359
|
"latin1",
|
|
2345
2360
|
"ascii"
|
|
2346
2361
|
]);
|
|
2347
|
-
const ENCODINGS = new Set([...TEXT_ENCODINGS, ...BINARY_ENCODINGS]);
|
|
2362
|
+
const ENCODINGS = /* @__PURE__ */ new Set([...TEXT_ENCODINGS, ...BINARY_ENCODINGS]);
|
|
2348
2363
|
const getCorrectEncoding = (encoding) => {
|
|
2349
2364
|
if (encoding === null) return "buffer";
|
|
2350
2365
|
if (typeof encoding !== "string") return;
|
|
@@ -2391,7 +2406,8 @@ const normalizeOptions = (filePath, rawArguments, rawOptions) => {
|
|
|
2391
2406
|
rawOptions.cwd = normalizeCwd(rawOptions.cwd);
|
|
2392
2407
|
const [processedFile, processedArguments, processedOptions] = handleNodeOption(filePath, rawArguments, rawOptions);
|
|
2393
2408
|
const { command: file, args: commandArguments, options: initialOptions } = import_cross_spawn.default._parse(processedFile, processedArguments, processedOptions);
|
|
2394
|
-
const
|
|
2409
|
+
const fdOptions = normalizeFdSpecificOptions(initialOptions);
|
|
2410
|
+
const options = addDefaultOptions(fdOptions);
|
|
2395
2411
|
validateTimeout(options);
|
|
2396
2412
|
validateEncoding(options);
|
|
2397
2413
|
validateIpcInputOption(options);
|
|
@@ -2592,14 +2608,17 @@ const getStreamContents$1 = async (stream, { init, convertChunk, getSize, trunca
|
|
|
2592
2608
|
const state = init();
|
|
2593
2609
|
state.length = 0;
|
|
2594
2610
|
try {
|
|
2595
|
-
for await (const chunk of asyncIterable)
|
|
2596
|
-
convertedChunk
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2611
|
+
for await (const chunk of asyncIterable) {
|
|
2612
|
+
const convertedChunk = convertChunk[getChunkType(chunk)](chunk, state);
|
|
2613
|
+
appendChunk({
|
|
2614
|
+
convertedChunk,
|
|
2615
|
+
state,
|
|
2616
|
+
getSize,
|
|
2617
|
+
truncateChunk,
|
|
2618
|
+
addChunk,
|
|
2619
|
+
maxBuffer
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2603
2622
|
appendFinalChunk({
|
|
2604
2623
|
state,
|
|
2605
2624
|
convertChunk,
|
|
@@ -2885,7 +2904,8 @@ const getErrorPrefix = ({ originalError, timedOut, timeout, isMaxBuffer, maxBuff
|
|
|
2885
2904
|
const getForcefulSuffix = (isForcefullyTerminated, forceKillAfterDelay) => isForcefullyTerminated ? ` and was forcefully terminated after ${forceKillAfterDelay} milliseconds` : "";
|
|
2886
2905
|
const getOriginalMessage = (originalError, cwd) => {
|
|
2887
2906
|
if (originalError instanceof DiscardedError) return;
|
|
2888
|
-
const
|
|
2907
|
+
const originalMessage = isExecaError(originalError) ? originalError.originalMessage : String(originalError?.message ?? originalError);
|
|
2908
|
+
const escapedOriginalMessage = escapeLines(fixCwdError(originalMessage, cwd));
|
|
2889
2909
|
return escapedOriginalMessage === "" ? void 0 : escapedOriginalMessage;
|
|
2890
2910
|
};
|
|
2891
2911
|
const serializeIpcMessage = (ipcMessage) => typeof ipcMessage === "string" ? ipcMessage : (0, node_util.inspect)(ipcMessage);
|
|
@@ -3096,26 +3116,28 @@ function prettyMilliseconds(milliseconds, options) {
|
|
|
3096
3116
|
add(Number(parsed.hours), "hour", "h");
|
|
3097
3117
|
}
|
|
3098
3118
|
add(Number(parsed.minutes), "minute", "m");
|
|
3099
|
-
if (!options.hideSeconds)
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3119
|
+
if (!options.hideSeconds) {
|
|
3120
|
+
if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3 && !options.subSecondsAsDecimals) {
|
|
3121
|
+
const seconds = Number(parsed.seconds);
|
|
3122
|
+
const milliseconds = Number(parsed.milliseconds);
|
|
3123
|
+
const microseconds = Number(parsed.microseconds);
|
|
3124
|
+
const nanoseconds = Number(parsed.nanoseconds);
|
|
3125
|
+
add(seconds, "second", "s");
|
|
3126
|
+
if (options.formatSubMilliseconds) {
|
|
3127
|
+
add(milliseconds, "millisecond", "ms");
|
|
3128
|
+
add(microseconds, "microsecond", "µs");
|
|
3129
|
+
add(nanoseconds, "nanosecond", "ns");
|
|
3130
|
+
} else {
|
|
3131
|
+
const millisecondsAndBelow = milliseconds + microseconds / 1e3 + nanoseconds / 1e6;
|
|
3132
|
+
const millisecondsDecimalDigits = typeof options.millisecondsDecimalDigits === "number" ? options.millisecondsDecimalDigits : 0;
|
|
3133
|
+
const millisecondsString = millisecondsDecimalDigits ? millisecondsAndBelow.toFixed(millisecondsDecimalDigits) : millisecondsAndBelow >= 1 ? Math.round(millisecondsAndBelow) : Math.ceil(millisecondsAndBelow);
|
|
3134
|
+
add(Number.parseFloat(millisecondsString), "millisecond", "ms", millisecondsString);
|
|
3135
|
+
}
|
|
3109
3136
|
} else {
|
|
3110
|
-
const
|
|
3111
|
-
const
|
|
3112
|
-
|
|
3113
|
-
add(Number.parseFloat(millisecondsString), "millisecond", "ms", millisecondsString);
|
|
3137
|
+
const secondsFixed = floorDecimals((isBigInt ? Number(milliseconds % ONE_DAY_IN_MILLISECONDS) : milliseconds) / 1e3 % 60, typeof options.secondsDecimalDigits === "number" ? options.secondsDecimalDigits : 1);
|
|
3138
|
+
const secondsString = options.keepDecimalsOnWholeSeconds ? secondsFixed : secondsFixed.replace(/\.0+$/, "");
|
|
3139
|
+
add(Number.parseFloat(secondsString), "second", "s", secondsString);
|
|
3114
3140
|
}
|
|
3115
|
-
} else {
|
|
3116
|
-
const secondsFixed = floorDecimals((isBigInt ? Number(milliseconds % ONE_DAY_IN_MILLISECONDS) : milliseconds) / 1e3 % 60, typeof options.secondsDecimalDigits === "number" ? options.secondsDecimalDigits : 1);
|
|
3117
|
-
const secondsString = options.keepDecimalsOnWholeSeconds ? secondsFixed : secondsFixed.replace(/\.0+$/, "");
|
|
3118
|
-
add(Number.parseFloat(secondsString), "second", "s", secondsString);
|
|
3119
3141
|
}
|
|
3120
3142
|
if (result.length === 0) return sign + "0" + (options.verbose ? " milliseconds" : "ms");
|
|
3121
3143
|
const separator = options.colonNotation ? ":" : " ";
|
|
@@ -3140,9 +3162,10 @@ const logResult = (result, verboseInfo) => {
|
|
|
3140
3162
|
logDuration(result, verboseInfo);
|
|
3141
3163
|
};
|
|
3142
3164
|
const logDuration = (result, verboseInfo) => {
|
|
3165
|
+
const verboseMessage = `(done in ${prettyMilliseconds(result.durationMs)})`;
|
|
3143
3166
|
verboseLog({
|
|
3144
3167
|
type: "duration",
|
|
3145
|
-
verboseMessage
|
|
3168
|
+
verboseMessage,
|
|
3146
3169
|
verboseInfo,
|
|
3147
3170
|
result
|
|
3148
3171
|
});
|
|
@@ -3210,10 +3233,10 @@ const isTransformOptions = (value) => isPlainObject(value) && (value.transform !
|
|
|
3210
3233
|
const isUrl = (value) => Object.prototype.toString.call(value) === "[object URL]";
|
|
3211
3234
|
const isRegularUrl = (value) => isUrl(value) && value.protocol !== "file:";
|
|
3212
3235
|
const isFilePathObject = (value) => isPlainObject(value) && Object.keys(value).length > 0 && Object.keys(value).every((key) => FILE_PATH_KEYS.has(key)) && isFilePathString(value.file);
|
|
3213
|
-
const FILE_PATH_KEYS = new Set(["file", "append"]);
|
|
3236
|
+
const FILE_PATH_KEYS = /* @__PURE__ */ new Set(["file", "append"]);
|
|
3214
3237
|
const isFilePathString = (file) => typeof file === "string";
|
|
3215
3238
|
const isUnknownStdioString = (type, value) => type === "native" && typeof value === "string" && !KNOWN_STDIO_STRINGS.has(value);
|
|
3216
|
-
const KNOWN_STDIO_STRINGS = new Set([
|
|
3239
|
+
const KNOWN_STDIO_STRINGS = /* @__PURE__ */ new Set([
|
|
3217
3240
|
"ipc",
|
|
3218
3241
|
"ignore",
|
|
3219
3242
|
"inherit",
|
|
@@ -3227,24 +3250,24 @@ const isTransformStream = (value) => isReadableStream(value?.readable) && isWrit
|
|
|
3227
3250
|
const isAsyncIterableObject = (value) => isObject(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
3228
3251
|
const isIterableObject = (value) => isObject(value) && typeof value[Symbol.iterator] === "function";
|
|
3229
3252
|
const isObject = (value) => typeof value === "object" && value !== null;
|
|
3230
|
-
const TRANSFORM_TYPES = new Set([
|
|
3253
|
+
const TRANSFORM_TYPES = /* @__PURE__ */ new Set([
|
|
3231
3254
|
"generator",
|
|
3232
3255
|
"asyncGenerator",
|
|
3233
3256
|
"duplex",
|
|
3234
3257
|
"webTransform"
|
|
3235
3258
|
]);
|
|
3236
|
-
const FILE_TYPES = new Set([
|
|
3259
|
+
const FILE_TYPES = /* @__PURE__ */ new Set([
|
|
3237
3260
|
"fileUrl",
|
|
3238
3261
|
"filePath",
|
|
3239
3262
|
"fileNumber"
|
|
3240
3263
|
]);
|
|
3241
|
-
const SPECIAL_DUPLICATE_TYPES_SYNC = new Set(["fileUrl", "filePath"]);
|
|
3242
|
-
const SPECIAL_DUPLICATE_TYPES = new Set([
|
|
3264
|
+
const SPECIAL_DUPLICATE_TYPES_SYNC = /* @__PURE__ */ new Set(["fileUrl", "filePath"]);
|
|
3265
|
+
const SPECIAL_DUPLICATE_TYPES = /* @__PURE__ */ new Set([
|
|
3243
3266
|
...SPECIAL_DUPLICATE_TYPES_SYNC,
|
|
3244
3267
|
"webStream",
|
|
3245
3268
|
"nodeStream"
|
|
3246
3269
|
]);
|
|
3247
|
-
const FORBID_DUPLICATE_TYPES = new Set(["webTransform", "duplex"]);
|
|
3270
|
+
const FORBID_DUPLICATE_TYPES = /* @__PURE__ */ new Set(["webTransform", "duplex"]);
|
|
3248
3271
|
const TYPE_TO_MESSAGE = {
|
|
3249
3272
|
generator: "a generator",
|
|
3250
3273
|
asyncGenerator: "an async generator",
|
|
@@ -3577,7 +3600,8 @@ const validateDuplicateStreamSync = ({ otherStdioItems, type, value, optionName,
|
|
|
3577
3600
|
const getDuplicateStreamInstance = ({ otherStdioItems, type, value, optionName, direction }) => {
|
|
3578
3601
|
const duplicateStdioItems = otherStdioItems.filter((stdioItem) => hasSameValue(stdioItem, value));
|
|
3579
3602
|
if (duplicateStdioItems.length === 0) return;
|
|
3580
|
-
|
|
3603
|
+
const differentStdioItem = duplicateStdioItems.find((stdioItem) => stdioItem.direction !== direction);
|
|
3604
|
+
throwOnDuplicateStream(differentStdioItem, optionName, type);
|
|
3581
3605
|
return direction === "output" ? duplicateStdioItems[0].stream : void 0;
|
|
3582
3606
|
};
|
|
3583
3607
|
const hasSameValue = ({ type, value }, secondValue) => {
|
|
@@ -3586,7 +3610,8 @@ const hasSameValue = ({ type, value }, secondValue) => {
|
|
|
3586
3610
|
return value === secondValue;
|
|
3587
3611
|
};
|
|
3588
3612
|
const validateDuplicateTransform = ({ otherStdioItems, type, value, optionName }) => {
|
|
3589
|
-
|
|
3613
|
+
const duplicateStdioItem = otherStdioItems.find(({ value: { transform } }) => transform === value.transform);
|
|
3614
|
+
throwOnDuplicateStream(duplicateStdioItem, optionName, type);
|
|
3590
3615
|
};
|
|
3591
3616
|
const throwOnDuplicateStream = (stdioItem, optionName, type) => {
|
|
3592
3617
|
if (stdioItem !== void 0) throw new TypeError(`The \`${stdioItem.optionName}\` and \`${optionName}\` options must not target ${TYPE_TO_MESSAGE[type]} that is the same.`);
|
|
@@ -3594,13 +3619,14 @@ const throwOnDuplicateStream = (stdioItem, optionName, type) => {
|
|
|
3594
3619
|
//#endregion
|
|
3595
3620
|
//#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/stdio/handle.js
|
|
3596
3621
|
const handleStdio = (addProperties, options, verboseInfo, isSync) => {
|
|
3622
|
+
const initialFileDescriptors = normalizeStdioOption(options, verboseInfo, isSync).map((stdioOption, fdNumber) => getFileDescriptor({
|
|
3623
|
+
stdioOption,
|
|
3624
|
+
fdNumber,
|
|
3625
|
+
options,
|
|
3626
|
+
isSync
|
|
3627
|
+
}));
|
|
3597
3628
|
const fileDescriptors = getFinalFileDescriptors({
|
|
3598
|
-
initialFileDescriptors
|
|
3599
|
-
stdioOption,
|
|
3600
|
-
fdNumber,
|
|
3601
|
-
options,
|
|
3602
|
-
isSync
|
|
3603
|
-
})),
|
|
3629
|
+
initialFileDescriptors,
|
|
3604
3630
|
addProperties,
|
|
3605
3631
|
options,
|
|
3606
3632
|
isSync
|
|
@@ -3617,13 +3643,14 @@ const getFileDescriptor = ({ stdioOption, fdNumber, options, isSync }) => {
|
|
|
3617
3643
|
optionName
|
|
3618
3644
|
});
|
|
3619
3645
|
const direction = getStreamDirection(initialStdioItems, fdNumber, optionName);
|
|
3620
|
-
const
|
|
3646
|
+
const stdioItems = initialStdioItems.map((stdioItem) => handleNativeStream({
|
|
3621
3647
|
stdioItem,
|
|
3622
3648
|
isStdioArray,
|
|
3623
3649
|
fdNumber,
|
|
3624
3650
|
direction,
|
|
3625
3651
|
isSync
|
|
3626
|
-
}))
|
|
3652
|
+
}));
|
|
3653
|
+
const normalizedStdioItems = normalizeTransforms(stdioItems, optionName, direction, options);
|
|
3627
3654
|
const objectMode = getFdObjectMode(normalizedStdioItems, direction);
|
|
3628
3655
|
validateFileObjectMode(normalizedStdioItems, objectMode);
|
|
3629
3656
|
return {
|
|
@@ -3633,7 +3660,8 @@ const getFileDescriptor = ({ stdioOption, fdNumber, options, isSync }) => {
|
|
|
3633
3660
|
};
|
|
3634
3661
|
};
|
|
3635
3662
|
const initializeStdioItems = ({ stdioOption, fdNumber, options, optionName }) => {
|
|
3636
|
-
const
|
|
3663
|
+
const initialStdioItems = [...(Array.isArray(stdioOption) ? stdioOption : [stdioOption]).map((value) => initializeStdioItem(value, optionName)), ...handleInputOptions(options, fdNumber)];
|
|
3664
|
+
const stdioItems = filterDuplicates(initialStdioItems);
|
|
3637
3665
|
const isStdioArray = stdioItems.length > 1;
|
|
3638
3666
|
validateStdioArray(stdioItems, isStdioArray, optionName);
|
|
3639
3667
|
validateStreams(stdioItems);
|
|
@@ -3652,7 +3680,7 @@ const validateStdioArray = (stdioItems, isStdioArray, optionName) => {
|
|
|
3652
3680
|
if (!isStdioArray) return;
|
|
3653
3681
|
for (const { value, optionName } of stdioItems) if (INVALID_STDIO_ARRAY_OPTIONS.has(value)) throw new Error(`The \`${optionName}\` option must not include \`${value}\`.`);
|
|
3654
3682
|
};
|
|
3655
|
-
const INVALID_STDIO_ARRAY_OPTIONS = new Set(["ignore", "ipc"]);
|
|
3683
|
+
const INVALID_STDIO_ARRAY_OPTIONS = /* @__PURE__ */ new Set(["ignore", "ipc"]);
|
|
3656
3684
|
const validateStreams = (stdioItems) => {
|
|
3657
3685
|
for (const stdioItem of stdioItems) validateFileStdio(stdioItem);
|
|
3658
3686
|
};
|
|
@@ -3981,7 +4009,10 @@ const generatorToStream = ({ value, value: { transform, final, writableObjectMod
|
|
|
3981
4009
|
const runGeneratorsSync = (chunks, stdioItems, encoding, isInput) => {
|
|
3982
4010
|
const generators = stdioItems.filter(({ type }) => type === "generator");
|
|
3983
4011
|
const reversedGenerators = isInput ? generators.reverse() : generators;
|
|
3984
|
-
for (const { value, optionName } of reversedGenerators)
|
|
4012
|
+
for (const { value, optionName } of reversedGenerators) {
|
|
4013
|
+
const generators = addInternalGenerators(value, encoding, optionName);
|
|
4014
|
+
chunks = runTransformSync(generators, chunks);
|
|
4015
|
+
}
|
|
3985
4016
|
return chunks;
|
|
3986
4017
|
};
|
|
3987
4018
|
const addInternalGenerators = ({ transform, final, binary, writableObjectMode, readableObjectMode, preserveNewlines }, encoding, optionName) => {
|
|
@@ -4017,7 +4048,8 @@ const addInputOptionSync = (fileDescriptors, fdNumber, options) => {
|
|
|
4017
4048
|
const [{ type, optionName }] = allStdioItems;
|
|
4018
4049
|
throw new TypeError(`Only the \`stdin\` option, not \`${optionName}\`, can be ${TYPE_TO_MESSAGE[type]} with synchronous methods.`);
|
|
4019
4050
|
}
|
|
4020
|
-
|
|
4051
|
+
const transformedContents = allStdioItems.map(({ contents }) => contents).map((contents) => applySingleInputGeneratorsSync(contents, stdioItems));
|
|
4052
|
+
options.input = joinToUint8Array(transformedContents);
|
|
4021
4053
|
};
|
|
4022
4054
|
const applySingleInputGeneratorsSync = (contents, stdioItems) => {
|
|
4023
4055
|
const newContents = runGeneratorsSync(contents, stdioItems, "utf8", true);
|
|
@@ -4032,7 +4064,7 @@ const validateSerializable = (newContents) => {
|
|
|
4032
4064
|
//#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/verbose/output.js
|
|
4033
4065
|
const shouldLogOutput = ({ stdioItems, encoding, verboseInfo, fdNumber }) => fdNumber !== "all" && isFullVerbose(verboseInfo, fdNumber) && !BINARY_ENCODINGS.has(encoding) && fdUsesVerbose(fdNumber) && (stdioItems.some(({ type, value }) => type === "native" && PIPED_STDIO_VALUES.has(value)) || stdioItems.every(({ type }) => TRANSFORM_TYPES.has(type)));
|
|
4034
4066
|
const fdUsesVerbose = (fdNumber) => fdNumber === 1 || fdNumber === 2;
|
|
4035
|
-
const PIPED_STDIO_VALUES = new Set(["pipe", "overlapped"]);
|
|
4067
|
+
const PIPED_STDIO_VALUES = /* @__PURE__ */ new Set(["pipe", "overlapped"]);
|
|
4036
4068
|
const logLines = async (linesIterable, stream, fdNumber, verboseInfo) => {
|
|
4037
4069
|
for await (const line of linesIterable) if (!isPipingStream(stream)) logLine(line, fdNumber, verboseInfo);
|
|
4038
4070
|
};
|
|
@@ -4041,9 +4073,10 @@ const logLinesSync = (linesArray, fdNumber, verboseInfo) => {
|
|
|
4041
4073
|
};
|
|
4042
4074
|
const isPipingStream = (stream) => stream._readableState.pipes.length > 0;
|
|
4043
4075
|
const logLine = (line, fdNumber, verboseInfo) => {
|
|
4076
|
+
const verboseMessage = serializeVerboseMessage(line);
|
|
4044
4077
|
verboseLog({
|
|
4045
4078
|
type: "output",
|
|
4046
|
-
verboseMessage
|
|
4079
|
+
verboseMessage,
|
|
4047
4080
|
fdNumber,
|
|
4048
4081
|
verboseInfo
|
|
4049
4082
|
});
|
|
@@ -4069,10 +4102,12 @@ const transformOutputSync = ({ fileDescriptors, syncResult: { output }, options,
|
|
|
4069
4102
|
};
|
|
4070
4103
|
const transformOutputResultSync = ({ result, fileDescriptors, fdNumber, state, outputFiles, isMaxBuffer, verboseInfo }, { buffer, encoding, lines, stripFinalNewline, maxBuffer }) => {
|
|
4071
4104
|
if (result === null) return;
|
|
4072
|
-
const
|
|
4105
|
+
const truncatedResult = truncateMaxBufferSync(result, isMaxBuffer, maxBuffer);
|
|
4106
|
+
const uint8ArrayResult = bufferToUint8Array(truncatedResult);
|
|
4073
4107
|
const { stdioItems, objectMode } = fileDescriptors[fdNumber];
|
|
4108
|
+
const chunks = runOutputGeneratorsSync([uint8ArrayResult], stdioItems, encoding, state);
|
|
4074
4109
|
const { serializedResult, finalResult = serializedResult } = serializeChunks({
|
|
4075
|
-
chunks
|
|
4110
|
+
chunks,
|
|
4076
4111
|
objectMode,
|
|
4077
4112
|
encoding,
|
|
4078
4113
|
lines,
|
|
@@ -4196,7 +4231,7 @@ const getResultError = (error, exitCode, signal) => {
|
|
|
4196
4231
|
//#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/methods/main-sync.js
|
|
4197
4232
|
const execaCoreSync = (rawFile, rawArguments, rawOptions) => {
|
|
4198
4233
|
const { file, commandArguments, command, escapedCommand, startTime, verboseInfo, options, fileDescriptors } = handleSyncArguments(rawFile, rawArguments, rawOptions);
|
|
4199
|
-
|
|
4234
|
+
const result = spawnSubprocessSync({
|
|
4200
4235
|
file,
|
|
4201
4236
|
commandArguments,
|
|
4202
4237
|
options,
|
|
@@ -4205,11 +4240,13 @@ const execaCoreSync = (rawFile, rawArguments, rawOptions) => {
|
|
|
4205
4240
|
verboseInfo,
|
|
4206
4241
|
fileDescriptors,
|
|
4207
4242
|
startTime
|
|
4208
|
-
})
|
|
4243
|
+
});
|
|
4244
|
+
return handleResult(result, verboseInfo, options);
|
|
4209
4245
|
};
|
|
4210
4246
|
const handleSyncArguments = (rawFile, rawArguments, rawOptions) => {
|
|
4211
4247
|
const { command, escapedCommand, startTime, verboseInfo } = handleCommand(rawFile, rawArguments, rawOptions);
|
|
4212
|
-
const
|
|
4248
|
+
const syncOptions = normalizeSyncOptions(rawOptions);
|
|
4249
|
+
const { file, commandArguments, options } = normalizeOptions(rawFile, rawArguments, syncOptions);
|
|
4213
4250
|
validateSyncOptions(options);
|
|
4214
4251
|
return {
|
|
4215
4252
|
file,
|
|
@@ -4254,14 +4291,16 @@ const spawnSubprocessSync = ({ file, commandArguments, options, command, escaped
|
|
|
4254
4291
|
isMaxBuffer,
|
|
4255
4292
|
verboseInfo
|
|
4256
4293
|
});
|
|
4294
|
+
const stdio = output.map((stdioOutput, fdNumber) => stripNewline(stdioOutput, options, fdNumber));
|
|
4295
|
+
const all = stripNewline(getAllSync(output, options), options, "all");
|
|
4257
4296
|
return getSyncResult({
|
|
4258
4297
|
error,
|
|
4259
4298
|
exitCode,
|
|
4260
4299
|
signal,
|
|
4261
4300
|
timedOut,
|
|
4262
4301
|
isMaxBuffer,
|
|
4263
|
-
stdio
|
|
4264
|
-
all
|
|
4302
|
+
stdio,
|
|
4303
|
+
all,
|
|
4265
4304
|
options,
|
|
4266
4305
|
command,
|
|
4267
4306
|
escapedCommand,
|
|
@@ -4271,7 +4310,8 @@ const spawnSubprocessSync = ({ file, commandArguments, options, command, escaped
|
|
|
4271
4310
|
const runSubprocessSync = ({ file, commandArguments, options, command, escapedCommand, fileDescriptors, startTime }) => {
|
|
4272
4311
|
try {
|
|
4273
4312
|
addInputOptionsSync(fileDescriptors, options);
|
|
4274
|
-
|
|
4313
|
+
const normalizedOptions = normalizeSpawnSyncOptions(options);
|
|
4314
|
+
return (0, node_child_process.spawnSync)(file, commandArguments, normalizedOptions);
|
|
4275
4315
|
} catch (error) {
|
|
4276
4316
|
return makeEarlyError({
|
|
4277
4317
|
error,
|
|
@@ -4485,17 +4525,18 @@ const handleEarlyError = ({ error, command, escapedCommand, fileDescriptors, opt
|
|
|
4485
4525
|
writable,
|
|
4486
4526
|
duplex
|
|
4487
4527
|
});
|
|
4528
|
+
const earlyError = makeEarlyError({
|
|
4529
|
+
error,
|
|
4530
|
+
command,
|
|
4531
|
+
escapedCommand,
|
|
4532
|
+
fileDescriptors,
|
|
4533
|
+
options,
|
|
4534
|
+
startTime,
|
|
4535
|
+
isSync: false
|
|
4536
|
+
});
|
|
4488
4537
|
return {
|
|
4489
4538
|
subprocess,
|
|
4490
|
-
promise: handleDummyPromise(
|
|
4491
|
-
error,
|
|
4492
|
-
command,
|
|
4493
|
-
escapedCommand,
|
|
4494
|
-
fileDescriptors,
|
|
4495
|
-
options,
|
|
4496
|
-
startTime,
|
|
4497
|
-
isSync: false
|
|
4498
|
-
}), verboseInfo, options)
|
|
4539
|
+
promise: handleDummyPromise(earlyError, verboseInfo, options)
|
|
4499
4540
|
};
|
|
4500
4541
|
};
|
|
4501
4542
|
const createDummyStreams = (subprocess, fileDescriptors) => {
|
|
@@ -4679,8 +4720,10 @@ const endWhenStreamsDone = async ({ passThroughStream, stream, streams, ended, a
|
|
|
4679
4720
|
controller.abort();
|
|
4680
4721
|
updateMaxListeners(passThroughStream, -PASSTHROUGH_LISTENERS_PER_STREAM);
|
|
4681
4722
|
}
|
|
4682
|
-
if (streams.size > 0 && streams.size === ended.size + aborted.size)
|
|
4683
|
-
|
|
4723
|
+
if (streams.size > 0 && streams.size === ended.size + aborted.size) {
|
|
4724
|
+
if (ended.size === 0 && aborted.size > 0) abortStream(passThroughStream);
|
|
4725
|
+
else endStream(passThroughStream);
|
|
4726
|
+
}
|
|
4684
4727
|
};
|
|
4685
4728
|
const afterMergedStreamFinished = async (onFinished, stream, { signal }) => {
|
|
4686
4729
|
try {
|
|
@@ -4786,7 +4829,10 @@ const pipeOutputAsync = (subprocess, fileDescriptors, controller) => {
|
|
|
4786
4829
|
controller
|
|
4787
4830
|
});
|
|
4788
4831
|
}
|
|
4789
|
-
for (const [outputStream, inputStreams] of pipeGroups.entries())
|
|
4832
|
+
for (const [outputStream, inputStreams] of pipeGroups.entries()) {
|
|
4833
|
+
const inputStream = inputStreams.length === 1 ? inputStreams[0] : mergeStreams(inputStreams);
|
|
4834
|
+
pipeStreams(inputStream, outputStream);
|
|
4835
|
+
}
|
|
4790
4836
|
};
|
|
4791
4837
|
const pipeTransform = (subprocess, stream, direction, fdNumber) => {
|
|
4792
4838
|
if (direction === "output") pipeStreams(subprocess.stdio[fdNumber], stream);
|
|
@@ -5275,12 +5321,13 @@ const stopReadingOnStreamEnd = async (onStreamEnd, controller, stream) => {
|
|
|
5275
5321
|
}
|
|
5276
5322
|
};
|
|
5277
5323
|
const iterateOnStream = ({ stream, controller, binary, shouldEncode, encoding, shouldSplit, preserveNewlines }) => {
|
|
5324
|
+
const onStdoutChunk = (0, node_events.on)(stream, "data", {
|
|
5325
|
+
signal: controller.signal,
|
|
5326
|
+
highWaterMark: HIGH_WATER_MARK,
|
|
5327
|
+
highWatermark: HIGH_WATER_MARK
|
|
5328
|
+
});
|
|
5278
5329
|
return iterateOnData({
|
|
5279
|
-
onStdoutChunk
|
|
5280
|
-
signal: controller.signal,
|
|
5281
|
-
highWaterMark: HIGH_WATER_MARK,
|
|
5282
|
-
highWatermark: HIGH_WATER_MARK
|
|
5283
|
-
}),
|
|
5330
|
+
onStdoutChunk,
|
|
5284
5331
|
controller,
|
|
5285
5332
|
binary,
|
|
5286
5333
|
shouldEncode,
|
|
@@ -5324,12 +5371,13 @@ const getStreamOutput = async ({ stream, onStreamEnd, fdNumber, encoding, buffer
|
|
|
5324
5371
|
await Promise.all([resumeStream(stream), logPromise]);
|
|
5325
5372
|
return;
|
|
5326
5373
|
}
|
|
5374
|
+
const stripFinalNewlineValue = getStripFinalNewline(stripFinalNewline, fdNumber);
|
|
5327
5375
|
const iterable = iterateForResult({
|
|
5328
5376
|
stream,
|
|
5329
5377
|
onStreamEnd,
|
|
5330
5378
|
lines,
|
|
5331
5379
|
encoding,
|
|
5332
|
-
stripFinalNewline:
|
|
5380
|
+
stripFinalNewline: stripFinalNewlineValue,
|
|
5333
5381
|
allMixed
|
|
5334
5382
|
});
|
|
5335
5383
|
const [output] = await Promise.all([getStreamContents({
|
|
@@ -5349,14 +5397,15 @@ const logOutputAsync = async ({ stream, onStreamEnd, fdNumber, encoding, allMixe
|
|
|
5349
5397
|
verboseInfo,
|
|
5350
5398
|
fdNumber
|
|
5351
5399
|
})) return;
|
|
5352
|
-
|
|
5400
|
+
const linesIterable = iterateForResult({
|
|
5353
5401
|
stream,
|
|
5354
5402
|
onStreamEnd,
|
|
5355
5403
|
lines: true,
|
|
5356
5404
|
encoding,
|
|
5357
5405
|
stripFinalNewline: true,
|
|
5358
5406
|
allMixed
|
|
5359
|
-
})
|
|
5407
|
+
});
|
|
5408
|
+
await logLines(linesIterable, stream, fdNumber, verboseInfo);
|
|
5360
5409
|
};
|
|
5361
5410
|
const resumeStream = async (stream) => {
|
|
5362
5411
|
await (0, node_timers_promises.setImmediate)();
|
|
@@ -5502,9 +5551,10 @@ const getAllMixed = ({ all, stdout, stderr }) => all && stdout && stderr && stdo
|
|
|
5502
5551
|
//#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/verbose/ipc.js
|
|
5503
5552
|
const shouldLogIpc = (verboseInfo) => isFullVerbose(verboseInfo, "ipc");
|
|
5504
5553
|
const logIpcOutput = (message, verboseInfo) => {
|
|
5554
|
+
const verboseMessage = serializeVerboseMessage(message);
|
|
5505
5555
|
verboseLog({
|
|
5506
5556
|
type: "ipc",
|
|
5507
|
-
verboseMessage
|
|
5557
|
+
verboseMessage,
|
|
5508
5558
|
fdNumber: "ipc",
|
|
5509
5559
|
verboseInfo
|
|
5510
5560
|
});
|
|
@@ -5900,14 +5950,15 @@ const onDuplexDestroy = async ({ subprocessStdout, subprocessStdin, subprocess,
|
|
|
5900
5950
|
const createIterable = (subprocess, encoding, { from, binary: binaryOption = false, preserveNewlines = false } = {}) => {
|
|
5901
5951
|
const binary = binaryOption || BINARY_ENCODINGS.has(encoding);
|
|
5902
5952
|
const subprocessStdout = getFromStream(subprocess, from);
|
|
5903
|
-
|
|
5953
|
+
const onStdoutData = iterateOnSubprocessStream({
|
|
5904
5954
|
subprocessStdout,
|
|
5905
5955
|
subprocess,
|
|
5906
5956
|
binary,
|
|
5907
5957
|
shouldEncode: true,
|
|
5908
5958
|
encoding,
|
|
5909
5959
|
preserveNewlines
|
|
5910
|
-
})
|
|
5960
|
+
});
|
|
5961
|
+
return iterateOnStdoutData(onStdoutData, subprocessStdout, subprocess);
|
|
5911
5962
|
};
|
|
5912
5963
|
const iterateOnStdoutData = async function* (onStdoutData, subprocessStdout, subprocess) {
|
|
5913
5964
|
try {
|
|
@@ -6067,19 +6118,22 @@ const handlePromise = async ({ subprocess, options, startTime, verboseInfo, file
|
|
|
6067
6118
|
});
|
|
6068
6119
|
controller.abort();
|
|
6069
6120
|
onInternalError.resolve();
|
|
6070
|
-
|
|
6121
|
+
const stdio = stdioResults.map((stdioResult, fdNumber) => stripNewline(stdioResult, options, fdNumber));
|
|
6122
|
+
const all = stripNewline(allResult, options, "all");
|
|
6123
|
+
const result = getAsyncResult({
|
|
6071
6124
|
errorInfo,
|
|
6072
6125
|
exitCode,
|
|
6073
6126
|
signal,
|
|
6074
|
-
stdio
|
|
6075
|
-
all
|
|
6127
|
+
stdio,
|
|
6128
|
+
all,
|
|
6076
6129
|
ipcOutput,
|
|
6077
6130
|
context,
|
|
6078
6131
|
options,
|
|
6079
6132
|
command,
|
|
6080
6133
|
escapedCommand,
|
|
6081
6134
|
startTime
|
|
6082
|
-
})
|
|
6135
|
+
});
|
|
6136
|
+
return handleResult(result, verboseInfo, options);
|
|
6083
6137
|
};
|
|
6084
6138
|
const getAsyncResult = ({ errorInfo, exitCode, signal, stdio, all, ipcOutput, context, options, command, escapedCommand, startTime }) => "error" in errorInfo ? makeError({
|
|
6085
6139
|
error: errorInfo.error,
|
|
@@ -6123,7 +6177,7 @@ const mergeOption = (optionName, boundOptionValue, optionValue) => {
|
|
|
6123
6177
|
};
|
|
6124
6178
|
return optionValue;
|
|
6125
6179
|
};
|
|
6126
|
-
const DEEP_OPTIONS = new Set(["env", ...FD_SPECIFIC_OPTIONS]);
|
|
6180
|
+
const DEEP_OPTIONS = /* @__PURE__ */ new Set(["env", ...FD_SPECIFIC_OPTIONS]);
|
|
6127
6181
|
//#endregion
|
|
6128
6182
|
//#region ../../node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/methods/create.js
|
|
6129
6183
|
const createExeca = (mapArguments, boundOptions, deepOptions, setBoundExeca) => {
|
|
@@ -6150,7 +6204,8 @@ const callBoundExeca = ({ mapArguments, deepOptions = {}, boundOptions = {}, set
|
|
|
6150
6204
|
return isSync ? execaCoreSync(file, commandArguments, options) : execaCoreAsync(file, commandArguments, options, createNested);
|
|
6151
6205
|
};
|
|
6152
6206
|
const parseArguments = ({ mapArguments, firstArgument, nextArguments, deepOptions, boundOptions }) => {
|
|
6153
|
-
const
|
|
6207
|
+
const callArguments = isTemplateString(firstArgument) ? parseTemplates(firstArgument, nextArguments) : [firstArgument, ...nextArguments];
|
|
6208
|
+
const [initialFile, initialArguments, initialOptions] = normalizeParameters(...callArguments);
|
|
6154
6209
|
const mergedOptions = mergeOptions(mergeOptions(deepOptions, boundOptions), initialOptions);
|
|
6155
6210
|
const { file = initialFile, commandArguments = initialArguments, options = mergedOptions, isSync = false } = mapArguments({
|
|
6156
6211
|
file: initialFile,
|
|
@@ -6221,8 +6276,8 @@ const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcEx
|
|
|
6221
6276
|
//#endregion
|
|
6222
6277
|
//#region src/cloudFrontInvalidation.ts
|
|
6223
6278
|
const DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
6224
|
-
const DEFAULT_TIMEOUT_MS =
|
|
6225
|
-
const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
6279
|
+
const DEFAULT_TIMEOUT_MS = 3e5;
|
|
6280
|
+
const sleep$1 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
6226
6281
|
const invalidateCloudFront = async (client, distributionId, paths, options) => {
|
|
6227
6282
|
if (paths.length === 0) return;
|
|
6228
6283
|
const response = await client.send(new _aws_sdk_client_cloudfront.CreateInvalidationCommand({
|
|
@@ -6240,7 +6295,7 @@ const invalidateCloudFront = async (client, distributionId, paths, options) => {
|
|
|
6240
6295
|
if (!invalidationId) throw new Error("CloudFront invalidation response is missing Invalidation.Id");
|
|
6241
6296
|
const deadline = Date.now() + DEFAULT_TIMEOUT_MS;
|
|
6242
6297
|
while (Date.now() < deadline) {
|
|
6243
|
-
await sleep(DEFAULT_POLL_INTERVAL_MS);
|
|
6298
|
+
await sleep$1(DEFAULT_POLL_INTERVAL_MS);
|
|
6244
6299
|
if ((await client.send(new _aws_sdk_client_cloudfront.GetInvalidationCommand({
|
|
6245
6300
|
DistributionId: distributionId,
|
|
6246
6301
|
Id: invalidationId
|
|
@@ -6248,1751 +6303,294 @@ const invalidateCloudFront = async (client, distributionId, paths, options) => {
|
|
|
6248
6303
|
}
|
|
6249
6304
|
throw new Error(`Timed out waiting for CloudFront invalidation ${invalidationId}.`);
|
|
6250
6305
|
};
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
const byteLength = new TextEncoder().encode(JSON.stringify(item)).byteLength;
|
|
6264
|
-
if (byteLength > 8192) throw new DynamoDBMetadataItemSizeError(byteLength);
|
|
6265
|
-
return item;
|
|
6266
|
-
};
|
|
6267
|
-
var DynamoDBCatalogItemSizeError = class extends Error {
|
|
6268
|
-
name = "DynamoDBCatalogItemSizeError";
|
|
6269
|
-
constructor(byteLength) {
|
|
6270
|
-
super(`DynamoDB catalog item is ${byteLength} bytes; maximum is ${DYNAMODB_MAX_CATALOG_ITEM_BYTES}`);
|
|
6271
|
-
this.byteLength = byteLength;
|
|
6272
|
-
}
|
|
6273
|
-
};
|
|
6274
|
-
const boundedDynamoDBCatalogItem = (item) => {
|
|
6275
|
-
const byteLength = new TextEncoder().encode(JSON.stringify(item)).byteLength;
|
|
6276
|
-
if (byteLength > 409600) throw new DynamoDBCatalogItemSizeError(byteLength);
|
|
6277
|
-
return item;
|
|
6278
|
-
};
|
|
6279
|
-
const DYNAMODB_CHANNEL_PARTITION = "channels";
|
|
6280
|
-
const DYNAMODB_CHANNEL_NAME_PARTITION = "_hot-updater#channel-names";
|
|
6281
|
-
var DynamoDBStoredItemError = class extends Error {
|
|
6282
|
-
name = "DynamoDBStoredItemError";
|
|
6283
|
-
constructor() {
|
|
6284
|
-
super("DynamoDB contains an invalid Hot Updater row");
|
|
6285
|
-
}
|
|
6286
|
-
};
|
|
6287
|
-
const field = (value, name) => Reflect.get(value, name);
|
|
6288
|
-
const isNullableString = (value) => value === null || typeof value === "string";
|
|
6289
|
-
const isByteSize = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
6290
|
-
const withLegacyArchiveByteSize = (value) => typeof value === "object" && value !== null && field(value, "archive_byte_size") === void 0 ? {
|
|
6291
|
-
...value,
|
|
6292
|
-
archive_byte_size: 0
|
|
6293
|
-
} : value;
|
|
6294
|
-
const isBundleRow = (value) => typeof value === "object" && value !== null && typeof field(value, "id") === "string" && (field(value, "platform") === "ios" || field(value, "platform") === "android") && typeof field(value, "file_hash") === "string" && isNullableString(field(value, "git_commit_hash")) && typeof field(value, "storage_uri") === "string" && isByteSize(field(value, "archive_byte_size")) && (0, _hot_updater_plugin_core.isDatabaseMetadataObject)(field(value, "metadata")) && isNullableString(field(value, "manifest_storage_uri")) && isNullableString(field(value, "manifest_file_hash")) && isNullableString(field(value, "asset_base_storage_uri"));
|
|
6295
|
-
const isPatchRow = (value) => typeof value === "object" && value !== null && typeof field(value, "id") === "string" && typeof field(value, "bundle_id") === "string" && typeof field(value, "base_bundle_id") === "string" && typeof field(value, "base_file_hash") === "string" && typeof field(value, "patch_file_hash") === "string" && typeof field(value, "patch_storage_uri") === "string" && isByteSize(field(value, "byte_size")) && typeof field(value, "order_index") === "number";
|
|
6296
|
-
const isChannelRow = (value) => typeof value === "object" && value !== null && typeof field(value, "id") === "string" && typeof field(value, "name") === "string";
|
|
6297
|
-
const isReleaseRow = (value) => typeof value === "object" && value !== null && typeof field(value, "id") === "string" && typeof field(value, "revision") === "number" && typeof field(value, "scope_key") === "string" && typeof field(value, "channel_id") === "string" && (field(value, "platform") === "ios" || field(value, "platform") === "android") && (field(value, "kind") === "BUNDLE" || field(value, "kind") === "EMBEDDED") && isNullableString(field(value, "bundle_id")) && (field(value, "strategy") === "APP_VERSION" || field(value, "strategy") === "FINGERPRINT") && isNullableString(field(value, "target_app_version")) && isNullableString(field(value, "fingerprint_hash")) && typeof field(value, "enabled") === "boolean" && typeof field(value, "should_force_update") === "boolean" && isNullableString(field(value, "message")) && typeof field(value, "rollout_cohort_count") === "number" && Array.isArray(field(value, "target_cohorts")) && field(value, "target_cohorts").every((cohort) => typeof cohort === "string") && (field(value, "operation") === "DEPLOY" || field(value, "operation") === "PROMOTE" || field(value, "operation") === "ROLLBACK") && isNullableString(field(value, "source_release_id")) && typeof field(value, "created_at_ms") === "number" && typeof field(value, "updated_at_ms") === "number";
|
|
6298
|
-
const isReleaseCatalogRow = (value) => typeof value === "object" && value !== null && typeof field(value, "scope_key") === "string" && typeof field(value, "catalog_id") === "string" && (field(value, "strategy") === "APP_VERSION" || field(value, "strategy") === "FINGERPRINT") && typeof field(value, "channel_id") === "string" && typeof field(value, "channel_key") === "string" && (field(value, "platform") === "ios" || field(value, "platform") === "android") && isNullableString(field(value, "fingerprint_hash")) && typeof field(value, "generation") === "number" && typeof field(value, "payload") === "string" && typeof field(value, "catalog_hash") === "string" && typeof field(value, "byte_size") === "number" && typeof field(value, "is_tombstone") === "boolean" && typeof field(value, "updated_at_ms") === "number";
|
|
6299
|
-
const parseDynamoDBItem = (value) => {
|
|
6300
|
-
const pk = value.pk;
|
|
6301
|
-
const sk = value.sk;
|
|
6302
|
-
const version = value.version;
|
|
6303
|
-
const relationCount = value.relation_count;
|
|
6304
|
-
const ownedPatchCount = value.owned_patch_count;
|
|
6305
|
-
const gsi1pk = value.gsi1pk;
|
|
6306
|
-
const gsi1sk = value.gsi1sk;
|
|
6307
|
-
const row = pk === "bundles" ? withLegacyArchiveByteSize(value.row) : value.row;
|
|
6308
|
-
if (typeof sk !== "string" || typeof version !== "number") throw new DynamoDBStoredItemError();
|
|
6309
|
-
if (pk === "channels" && typeof value.reference_count === "number" && isChannelRow(row)) return {
|
|
6310
|
-
pk,
|
|
6311
|
-
sk,
|
|
6312
|
-
version,
|
|
6313
|
-
reference_count: value.reference_count,
|
|
6314
|
-
row
|
|
6315
|
-
};
|
|
6316
|
-
if (typeof pk === "string" && pk.startsWith("release-scope#") && isReleaseRow(row)) return {
|
|
6317
|
-
pk,
|
|
6318
|
-
sk,
|
|
6319
|
-
version,
|
|
6320
|
-
row
|
|
6321
|
-
};
|
|
6322
|
-
if (pk === "release_catalogs" && isReleaseCatalogRow(row)) return {
|
|
6323
|
-
pk,
|
|
6324
|
-
sk,
|
|
6325
|
-
version,
|
|
6326
|
-
row
|
|
6327
|
-
};
|
|
6328
|
-
if (typeof gsi1pk !== "string" || typeof gsi1sk !== "string") throw new DynamoDBStoredItemError();
|
|
6329
|
-
if (pk === "bundles" && isBundleRow(row)) {
|
|
6330
|
-
if (typeof relationCount !== "number" || typeof ownedPatchCount !== "number") throw new DynamoDBStoredItemError();
|
|
6331
|
-
return {
|
|
6332
|
-
pk,
|
|
6333
|
-
sk,
|
|
6334
|
-
version,
|
|
6335
|
-
relation_count: relationCount,
|
|
6336
|
-
owned_patch_count: ownedPatchCount,
|
|
6337
|
-
gsi1pk,
|
|
6338
|
-
gsi1sk,
|
|
6339
|
-
row
|
|
6340
|
-
};
|
|
6341
|
-
}
|
|
6342
|
-
if (pk === "bundle_patches" && isPatchRow(row)) return {
|
|
6343
|
-
pk,
|
|
6344
|
-
sk,
|
|
6345
|
-
version,
|
|
6346
|
-
gsi1pk,
|
|
6347
|
-
gsi1sk,
|
|
6348
|
-
row
|
|
6349
|
-
};
|
|
6350
|
-
throw new DynamoDBStoredItemError();
|
|
6351
|
-
};
|
|
6352
|
-
const bundlePlatformPartition = (row) => `bundle#${row.platform}`;
|
|
6353
|
-
const patchOwnerPartition = (bundleId) => `patch-owner#${bundleId}`;
|
|
6354
|
-
const patchOrderKey = (row) => `${row.order_index.toString().padStart(10, "0")}#${row.id}`;
|
|
6355
|
-
const toDynamoDBBundleItem = (row, version = 1, relationCount = 0, ownedPatchCount = 0) => ({
|
|
6356
|
-
pk: "bundles",
|
|
6357
|
-
sk: row.id,
|
|
6358
|
-
version,
|
|
6359
|
-
relation_count: relationCount,
|
|
6360
|
-
owned_patch_count: ownedPatchCount,
|
|
6361
|
-
gsi1pk: bundlePlatformPartition(row),
|
|
6362
|
-
gsi1sk: row.id,
|
|
6363
|
-
row
|
|
6364
|
-
});
|
|
6365
|
-
const toDynamoDBPatchItem = (row, version = 1) => {
|
|
6366
|
-
const orderKey = patchOrderKey(row);
|
|
6306
|
+
/**
|
|
6307
|
+
* The update-check routes' CloudFront invalidation: `invalidate` purges their
|
|
6308
|
+
* cached copies and `destroy` releases the client; undefined without a
|
|
6309
|
+
* distribution. A failed invalidation only warns, so the write that changed
|
|
6310
|
+
* the routes still succeeds.
|
|
6311
|
+
*/
|
|
6312
|
+
const createUpdateRouteInvalidation = ({ apiBasePath = "/release-catalogs", cloudfrontDistributionId, shouldWaitForInvalidation = false, credentials, region }) => {
|
|
6313
|
+
if (!cloudfrontDistributionId) return void 0;
|
|
6314
|
+
const client = new _aws_sdk_client_cloudfront.CloudFrontClient({
|
|
6315
|
+
credentials,
|
|
6316
|
+
region
|
|
6317
|
+
});
|
|
6367
6318
|
return {
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
};
|
|
6376
|
-
const toDynamoDBChannelItem = (row, version = 1, referenceCount = 0) => ({
|
|
6377
|
-
pk: DYNAMODB_CHANNEL_PARTITION,
|
|
6378
|
-
sk: row.id,
|
|
6379
|
-
version,
|
|
6380
|
-
reference_count: referenceCount,
|
|
6381
|
-
row
|
|
6382
|
-
});
|
|
6383
|
-
const releaseScopePartition = (scopeKey) => `release-scope#${scopeKey}`;
|
|
6384
|
-
const toDynamoDBReleaseItem = (row, version = 1) => ({
|
|
6385
|
-
pk: releaseScopePartition(row.scope_key),
|
|
6386
|
-
sk: row.id,
|
|
6387
|
-
version,
|
|
6388
|
-
row
|
|
6389
|
-
});
|
|
6390
|
-
const toDynamoDBReleaseCatalogItem = (row, version = 1) => ({
|
|
6391
|
-
pk: "release_catalogs",
|
|
6392
|
-
sk: row.scope_key,
|
|
6393
|
-
version,
|
|
6394
|
-
row
|
|
6395
|
-
});
|
|
6396
|
-
const itemKey = (model, id) => ({
|
|
6397
|
-
pk: model,
|
|
6398
|
-
sk: id
|
|
6399
|
-
});
|
|
6400
|
-
const exactDynamoDBId = (where) => {
|
|
6401
|
-
if (where?.length !== 1) return void 0;
|
|
6402
|
-
const condition = where[0];
|
|
6403
|
-
if (Reflect.get(condition, "field") !== "id" || (Reflect.get(condition, "operator") ?? "eq") !== "eq" || Reflect.get(condition, "mode") === "insensitive") return;
|
|
6404
|
-
const value = Reflect.get(condition, "value");
|
|
6405
|
-
return typeof value === "string" ? value : void 0;
|
|
6406
|
-
};
|
|
6407
|
-
const exactDynamoDBPatchOwner = (where) => {
|
|
6408
|
-
const owners = exactDynamoDBPatchOwners(where);
|
|
6409
|
-
return owners?.length === 1 ? owners[0] : void 0;
|
|
6410
|
-
};
|
|
6411
|
-
const exactDynamoDBPatchOwners = (where) => {
|
|
6412
|
-
if (!where || where.some((condition) => Reflect.get(condition, "connector") === "OR")) return;
|
|
6413
|
-
const ownerConditions = where.filter((condition) => Reflect.get(condition, "field") === "bundle_id");
|
|
6414
|
-
if (ownerConditions.length !== 1) return void 0;
|
|
6415
|
-
const condition = ownerConditions[0];
|
|
6416
|
-
if (Reflect.get(condition, "mode") === "insensitive") return;
|
|
6417
|
-
const operator = Reflect.get(condition, "operator") ?? "eq";
|
|
6418
|
-
const value = Reflect.get(condition, "value");
|
|
6419
|
-
if (operator === "eq") return typeof value === "string" ? [value] : void 0;
|
|
6420
|
-
if (operator === "in" && Array.isArray(value) && value.every((id) => typeof id === "string")) return value;
|
|
6421
|
-
};
|
|
6422
|
-
const exactDynamoDBBundleIds = (where) => {
|
|
6423
|
-
if (!where || where.some((condition) => Reflect.get(condition, "connector") === "OR")) return;
|
|
6424
|
-
for (const condition of where) {
|
|
6425
|
-
if (Reflect.get(condition, "field") !== "id" || Reflect.get(condition, "mode") === "insensitive") continue;
|
|
6426
|
-
const operator = Reflect.get(condition, "operator") ?? "eq";
|
|
6427
|
-
const value = Reflect.get(condition, "value");
|
|
6428
|
-
if (operator === "eq" && typeof value === "string") return [value];
|
|
6429
|
-
if (operator === "in" && Array.isArray(value) && value.every((id) => typeof id === "string")) return value;
|
|
6430
|
-
}
|
|
6431
|
-
};
|
|
6432
|
-
const dynamoDBBundlePageStart = (where, direction) => {
|
|
6433
|
-
if ((where ?? []).some((condition) => Reflect.get(condition, "connector") === "OR")) return;
|
|
6434
|
-
let result;
|
|
6435
|
-
for (const condition of where ?? []) {
|
|
6436
|
-
if (Reflect.get(condition, "field") !== "id") continue;
|
|
6437
|
-
const operator = Reflect.get(condition, "operator") ?? "eq";
|
|
6438
|
-
const value = Reflect.get(condition, "value");
|
|
6439
|
-
if (!(direction === "asc" ? operator === "gt" || operator === "gte" : operator === "lt" || operator === "lte") || typeof value !== "string") continue;
|
|
6440
|
-
const inclusive = operator === "gte" || operator === "lte";
|
|
6441
|
-
if (result === void 0 || (direction === "asc" ? value > result.id : value < result.id) || value === result.id && !inclusive) result = {
|
|
6442
|
-
id: value,
|
|
6443
|
-
inclusive
|
|
6444
|
-
};
|
|
6445
|
-
}
|
|
6446
|
-
return result;
|
|
6447
|
-
};
|
|
6448
|
-
const parseStoredItem = (item) => parseDynamoDBItem(item.pk === "release_catalogs" ? boundedDynamoDBCatalogItem(item) : boundedDynamoDBMetadataItem(item));
|
|
6449
|
-
const queryItems = async (store, input) => {
|
|
6450
|
-
let exclusiveStartKey;
|
|
6451
|
-
const items = [];
|
|
6452
|
-
do {
|
|
6453
|
-
const sortCondition = input.minimumSortKey ? " AND #sortKey >= :minimumSortKey" : "";
|
|
6454
|
-
const page = await store.client.send(new _aws_sdk_lib_dynamodb.QueryCommand({
|
|
6455
|
-
TableName: store.tableName,
|
|
6456
|
-
...input.indexName ? { IndexName: input.indexName } : {},
|
|
6457
|
-
...input.consistentRead ? { ConsistentRead: true } : {},
|
|
6458
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
6459
|
-
KeyConditionExpression: `#partitionKey = :partition${sortCondition}`,
|
|
6460
|
-
ExpressionAttributeNames: {
|
|
6461
|
-
"#partitionKey": input.indexName ? "gsi1pk" : "pk",
|
|
6462
|
-
...input.minimumSortKey ? { "#sortKey": input.indexName ? "gsi1sk" : "sk" } : {}
|
|
6463
|
-
},
|
|
6464
|
-
ExpressionAttributeValues: {
|
|
6465
|
-
":partition": input.partition,
|
|
6466
|
-
...input.minimumSortKey ? { ":minimumSortKey": input.minimumSortKey } : {}
|
|
6319
|
+
invalidate: async () => {
|
|
6320
|
+
try {
|
|
6321
|
+
await invalidateCloudFront(client, cloudfrontDistributionId, [`${apiBasePath.replace(/\/+$/, "")}/*`], { shouldWait: shouldWaitForInvalidation });
|
|
6322
|
+
} catch (error) {
|
|
6323
|
+
console.warn("[hot-updater/aws] CloudFront invalidation failed; continuing without cache invalidation.", {
|
|
6324
|
+
distributionId: cloudfrontDistributionId,
|
|
6325
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
6326
|
+
});
|
|
6467
6327
|
}
|
|
6468
|
-
}));
|
|
6469
|
-
const pageItems = (page.Items ?? []).map(parseStoredItem);
|
|
6470
|
-
items.push(...pageItems);
|
|
6471
|
-
exclusiveStartKey = page.LastEvaluatedKey;
|
|
6472
|
-
} while (exclusiveStartKey !== void 0);
|
|
6473
|
-
return items;
|
|
6474
|
-
};
|
|
6475
|
-
const queryBundleItemsPage = async (store, input) => {
|
|
6476
|
-
if (input.limit === 0) return [];
|
|
6477
|
-
let exclusiveStartKey;
|
|
6478
|
-
const items = [];
|
|
6479
|
-
do {
|
|
6480
|
-
const startOperator = input.start?.inclusive ? input.direction === "asc" ? ">=" : "<=" : input.direction === "asc" ? ">" : "<";
|
|
6481
|
-
const page = await store.client.send(new _aws_sdk_lib_dynamodb.QueryCommand({
|
|
6482
|
-
TableName: store.tableName,
|
|
6483
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
6484
|
-
KeyConditionExpression: input.start ? `#pk = :pk AND #sk ${startOperator} :start` : "#pk = :pk",
|
|
6485
|
-
ExpressionAttributeNames: {
|
|
6486
|
-
"#pk": "pk",
|
|
6487
|
-
...input.start ? { "#sk": "sk" } : {}
|
|
6488
|
-
},
|
|
6489
|
-
ExpressionAttributeValues: {
|
|
6490
|
-
":pk": "bundles",
|
|
6491
|
-
...input.start ? { ":start": input.start.id } : {}
|
|
6492
|
-
},
|
|
6493
|
-
Limit: Math.max(input.limit - items.length, 100),
|
|
6494
|
-
ScanIndexForward: input.direction === "asc"
|
|
6495
|
-
}));
|
|
6496
|
-
for (const item of (page.Items ?? []).map(parseStoredItem)) {
|
|
6497
|
-
if (item.pk === "bundles" && input.matches(item.row)) items.push(item);
|
|
6498
|
-
if (items.length === input.limit) return items;
|
|
6499
|
-
}
|
|
6500
|
-
exclusiveStartKey = page.LastEvaluatedKey;
|
|
6501
|
-
} while (exclusiveStartKey !== void 0);
|
|
6502
|
-
return items;
|
|
6503
|
-
};
|
|
6504
|
-
const loadModelItems = (store, model) => queryItems(store, {
|
|
6505
|
-
partition: model,
|
|
6506
|
-
consistentRead: true
|
|
6507
|
-
});
|
|
6508
|
-
const loadModelItem = async (store, model, id) => {
|
|
6509
|
-
const { Item } = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
6510
|
-
TableName: store.tableName,
|
|
6511
|
-
Key: itemKey(model, id),
|
|
6512
|
-
ConsistentRead: true
|
|
6513
|
-
}));
|
|
6514
|
-
return Item === void 0 ? void 0 : parseStoredItem(Item);
|
|
6515
|
-
};
|
|
6516
|
-
const loadBundleItem = async (store, id) => {
|
|
6517
|
-
const item = await loadModelItem(store, "bundles", id);
|
|
6518
|
-
return item?.pk === "bundles" ? item : void 0;
|
|
6519
|
-
};
|
|
6520
|
-
const loadPatchItem = async (store, id) => {
|
|
6521
|
-
const item = await loadModelItem(store, "bundle_patches", id);
|
|
6522
|
-
return item?.pk === "bundle_patches" ? item : void 0;
|
|
6523
|
-
};
|
|
6524
|
-
const loadChannelItem = async (store, id) => {
|
|
6525
|
-
const item = await loadModelItem(store, DYNAMODB_CHANNEL_PARTITION, id);
|
|
6526
|
-
return item?.pk === "channels" ? item : void 0;
|
|
6527
|
-
};
|
|
6528
|
-
const loadBundleItems = async (store) => {
|
|
6529
|
-
return (await loadModelItems(store, "bundles")).filter((item) => item.pk === "bundles");
|
|
6530
|
-
};
|
|
6531
|
-
const loadPatchItems = async (store) => {
|
|
6532
|
-
return (await loadModelItems(store, "bundle_patches")).filter((item) => item.pk === "bundle_patches");
|
|
6533
|
-
};
|
|
6534
|
-
const loadChannelItems = async (store) => {
|
|
6535
|
-
return (await loadModelItems(store, DYNAMODB_CHANNEL_PARTITION)).filter((item) => item.pk === DYNAMODB_CHANNEL_PARTITION);
|
|
6536
|
-
};
|
|
6537
|
-
const DYNAMODB_RELEASE_ID_PARTITION = "_hot-updater#release-scope-by-id";
|
|
6538
|
-
const releaseLocatorItem = (row) => ({
|
|
6539
|
-
pk: DYNAMODB_RELEASE_ID_PARTITION,
|
|
6540
|
-
sk: row.id,
|
|
6541
|
-
scope_key: row.scope_key
|
|
6542
|
-
});
|
|
6543
|
-
const loadReleaseItem = async (store, id) => {
|
|
6544
|
-
const locator = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
6545
|
-
TableName: store.tableName,
|
|
6546
|
-
Key: {
|
|
6547
|
-
pk: DYNAMODB_RELEASE_ID_PARTITION,
|
|
6548
|
-
sk: id
|
|
6549
6328
|
},
|
|
6550
|
-
|
|
6551
|
-
}));
|
|
6552
|
-
if (locator.Item === void 0) return void 0;
|
|
6553
|
-
const scopeKey = locator.Item.scope_key;
|
|
6554
|
-
if (typeof scopeKey !== "string") throw new DynamoDBStoredItemError();
|
|
6555
|
-
const result = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
6556
|
-
TableName: store.tableName,
|
|
6557
|
-
Key: {
|
|
6558
|
-
pk: releaseScopePartition(scopeKey),
|
|
6559
|
-
sk: id
|
|
6560
|
-
},
|
|
6561
|
-
ConsistentRead: true
|
|
6562
|
-
}));
|
|
6563
|
-
if (result.Item === void 0) throw new DynamoDBStoredItemError();
|
|
6564
|
-
const item = parseStoredItem(result.Item);
|
|
6565
|
-
if (!isReleaseRow(item.row) || item.sk !== id) throw new DynamoDBStoredItemError();
|
|
6566
|
-
return item;
|
|
6567
|
-
};
|
|
6568
|
-
const loadReleaseItemsByScope = async (store, scopeKey) => (await queryItems(store, {
|
|
6569
|
-
partition: releaseScopePartition(scopeKey),
|
|
6570
|
-
consistentRead: true
|
|
6571
|
-
})).filter((item) => item.pk === releaseScopePartition(scopeKey) && isReleaseRow(item.row));
|
|
6572
|
-
const loadReleaseItems = async (store) => {
|
|
6573
|
-
let exclusiveStartKey;
|
|
6574
|
-
const items = [];
|
|
6575
|
-
do {
|
|
6576
|
-
const page = await store.client.send(new _aws_sdk_lib_dynamodb.ScanCommand({
|
|
6577
|
-
TableName: store.tableName,
|
|
6578
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
6579
|
-
ConsistentRead: true,
|
|
6580
|
-
FilterExpression: "begins_with(#pk, :prefix)",
|
|
6581
|
-
ExpressionAttributeNames: { "#pk": "pk" },
|
|
6582
|
-
ExpressionAttributeValues: { ":prefix": "release-scope#" }
|
|
6583
|
-
}));
|
|
6584
|
-
for (const value of page.Items ?? []) {
|
|
6585
|
-
const item = parseStoredItem(value);
|
|
6586
|
-
if (isReleaseRow(item.row)) items.push(item);
|
|
6587
|
-
}
|
|
6588
|
-
exclusiveStartKey = page.LastEvaluatedKey;
|
|
6589
|
-
} while (exclusiveStartKey !== void 0);
|
|
6590
|
-
return items;
|
|
6591
|
-
};
|
|
6592
|
-
const loadReleaseCatalogItem = async (store, scopeKey) => {
|
|
6593
|
-
const result = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
6594
|
-
TableName: store.tableName,
|
|
6595
|
-
Key: itemKey("release_catalogs", scopeKey),
|
|
6596
|
-
ConsistentRead: true
|
|
6597
|
-
}));
|
|
6598
|
-
if (result.Item === void 0) return void 0;
|
|
6599
|
-
const item = parseStoredItem(result.Item);
|
|
6600
|
-
return item.pk === "release_catalogs" ? item : void 0;
|
|
6601
|
-
};
|
|
6602
|
-
const loadReleaseCatalogItems = async (store) => (await queryItems(store, {
|
|
6603
|
-
partition: "release_catalogs",
|
|
6604
|
-
consistentRead: true
|
|
6605
|
-
})).filter((item) => item.pk === "release_catalogs");
|
|
6606
|
-
const loadChannelByName = async (store, name) => {
|
|
6607
|
-
const { Item } = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
6608
|
-
TableName: store.tableName,
|
|
6609
|
-
Key: {
|
|
6610
|
-
pk: DYNAMODB_CHANNEL_NAME_PARTITION,
|
|
6611
|
-
sk: name
|
|
6612
|
-
},
|
|
6613
|
-
ConsistentRead: true
|
|
6614
|
-
}));
|
|
6615
|
-
const id = Item?.channel_id;
|
|
6616
|
-
if (Item === void 0) return void 0;
|
|
6617
|
-
if (typeof id !== "string") throw new DynamoDBStoredItemError();
|
|
6618
|
-
const channel = await loadChannelItem(store, id);
|
|
6619
|
-
if (channel === void 0 || channel.row.name !== name) throw new DynamoDBStoredItemError();
|
|
6620
|
-
return channel;
|
|
6621
|
-
};
|
|
6622
|
-
const insertDynamoDBChannel = async (store, input) => {
|
|
6623
|
-
const existing = await loadChannelByName(store, input.row.name);
|
|
6624
|
-
if (existing !== void 0) return {
|
|
6625
|
-
row: existing.row,
|
|
6626
|
-
inserted: false
|
|
6627
|
-
};
|
|
6628
|
-
const existingId = await loadChannelItem(store, input.row.id);
|
|
6629
|
-
if (existingId !== void 0) {
|
|
6630
|
-
if (existingId.row.name === input.row.name) return {
|
|
6631
|
-
row: existingId.row,
|
|
6632
|
-
inserted: false
|
|
6633
|
-
};
|
|
6634
|
-
throw new DynamoDBStoredItemError();
|
|
6635
|
-
}
|
|
6636
|
-
try {
|
|
6637
|
-
await commitDynamoDBTransaction(store, [{ Put: {
|
|
6638
|
-
TableName: store.tableName,
|
|
6639
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBChannelItem(input.row)),
|
|
6640
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
6641
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
6642
|
-
} }, { Put: {
|
|
6643
|
-
TableName: store.tableName,
|
|
6644
|
-
Item: {
|
|
6645
|
-
pk: DYNAMODB_CHANNEL_NAME_PARTITION,
|
|
6646
|
-
sk: input.row.name,
|
|
6647
|
-
channel_id: input.row.id
|
|
6648
|
-
},
|
|
6649
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
6650
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
6651
|
-
} }]);
|
|
6652
|
-
return {
|
|
6653
|
-
row: input.row,
|
|
6654
|
-
inserted: true
|
|
6655
|
-
};
|
|
6656
|
-
} catch (error) {
|
|
6657
|
-
const canonical = await loadChannelByName(store, input.row.name);
|
|
6658
|
-
if (canonical !== void 0) return {
|
|
6659
|
-
row: canonical.row,
|
|
6660
|
-
inserted: false
|
|
6661
|
-
};
|
|
6662
|
-
throw error;
|
|
6663
|
-
}
|
|
6664
|
-
};
|
|
6665
|
-
const deleteDynamoDBChannel = async (store, input) => {
|
|
6666
|
-
const current = await loadChannelItem(store, input.id);
|
|
6667
|
-
if (current === void 0) return {
|
|
6668
|
-
deleted: false,
|
|
6669
|
-
reason: "not_found"
|
|
6670
|
-
};
|
|
6671
|
-
if (current.reference_count !== 0) return {
|
|
6672
|
-
deleted: false,
|
|
6673
|
-
reason: "not_empty"
|
|
6329
|
+
destroy: () => client.destroy()
|
|
6674
6330
|
};
|
|
6675
|
-
try {
|
|
6676
|
-
await commitDynamoDBTransaction(store, [{ Delete: {
|
|
6677
|
-
TableName: store.tableName,
|
|
6678
|
-
Key: itemKey(DYNAMODB_CHANNEL_PARTITION, current.sk),
|
|
6679
|
-
ConditionExpression: "#version = :version AND #referenceCount = :zero",
|
|
6680
|
-
ExpressionAttributeNames: {
|
|
6681
|
-
"#version": "version",
|
|
6682
|
-
"#referenceCount": "reference_count"
|
|
6683
|
-
},
|
|
6684
|
-
ExpressionAttributeValues: {
|
|
6685
|
-
":version": current.version,
|
|
6686
|
-
":zero": 0
|
|
6687
|
-
}
|
|
6688
|
-
} }, { Delete: {
|
|
6689
|
-
TableName: store.tableName,
|
|
6690
|
-
Key: {
|
|
6691
|
-
pk: DYNAMODB_CHANNEL_NAME_PARTITION,
|
|
6692
|
-
sk: current.row.name
|
|
6693
|
-
},
|
|
6694
|
-
ConditionExpression: "#channelId = :channelId",
|
|
6695
|
-
ExpressionAttributeNames: { "#channelId": "channel_id" },
|
|
6696
|
-
ExpressionAttributeValues: { ":channelId": current.sk }
|
|
6697
|
-
} }]);
|
|
6698
|
-
return { deleted: true };
|
|
6699
|
-
} catch (error) {
|
|
6700
|
-
const latest = await loadChannelItem(store, input.id);
|
|
6701
|
-
if (latest !== void 0 && latest.reference_count > 0) return {
|
|
6702
|
-
deleted: false,
|
|
6703
|
-
reason: "not_empty"
|
|
6704
|
-
};
|
|
6705
|
-
if (latest === void 0) return {
|
|
6706
|
-
deleted: false,
|
|
6707
|
-
reason: "not_found"
|
|
6708
|
-
};
|
|
6709
|
-
throw error;
|
|
6710
|
-
}
|
|
6711
6331
|
};
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
const
|
|
6726
|
-
|
|
6727
|
-
|
|
6332
|
+
//#endregion
|
|
6333
|
+
//#region src/dynamoDBStore.ts
|
|
6334
|
+
/** DynamoDB's limits: 100 items and 4 MB a transaction, 400 KB an item, and 2,048- and 1,024-byte keys. */
|
|
6335
|
+
const DYNAMODB_LIMITS = {
|
|
6336
|
+
items: 100,
|
|
6337
|
+
bytes: 4e6,
|
|
6338
|
+
itemBytes: 4e5,
|
|
6339
|
+
keyBytes: {
|
|
6340
|
+
pk: 2048,
|
|
6341
|
+
sk: 1024
|
|
6342
|
+
}
|
|
6343
|
+
};
|
|
6344
|
+
/** BatchGetItem takes 100 keys a call. */
|
|
6345
|
+
const GET_BATCH = 100;
|
|
6346
|
+
/** A column named like a key attribute is stored with a leading dot, which no column name has. */
|
|
6347
|
+
const attribute = (column) => column === "pk" || column === "sk" ? `.${column}` : column;
|
|
6348
|
+
const renamed = (record, rename) => Object.fromEntries(Object.entries(record).map(([name, value]) => [rename(name), value]));
|
|
6349
|
+
const toItem = (key, row) => ({
|
|
6350
|
+
...renamed(row, attribute),
|
|
6351
|
+
pk: key.pk,
|
|
6352
|
+
sk: key.sk
|
|
6353
|
+
});
|
|
6354
|
+
const toRow = ({ pk: _pk, sk: _sk, ...item }) => renamed(item, (name) => name.replace(/^\./, ""));
|
|
6355
|
+
const idOf = ({ pk, sk }) => JSON.stringify([pk, sk]);
|
|
6356
|
+
const nameOf = (error) => error.name;
|
|
6357
|
+
const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
6358
|
+
/**
|
|
6359
|
+
* One transaction item. Its expressions bind every column name and value, so
|
|
6360
|
+
* no column clashes with a reserved word; `pk` and `sk` are none.
|
|
6361
|
+
*/
|
|
6362
|
+
const toTransactItem = (tableName, op) => {
|
|
6728
6363
|
const names = {};
|
|
6729
6364
|
const values = {};
|
|
6730
|
-
const
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
const valueName = model === "bundles" ? "bundle" : "patch";
|
|
6735
|
-
names[`#${name}`] = name;
|
|
6736
|
-
values[`:${valueName}Delta`] = change;
|
|
6737
|
-
updates.push(`#${name} :${valueName}Delta`);
|
|
6738
|
-
if (change < 0) {
|
|
6739
|
-
values[`:${valueName}Removal`] = -change;
|
|
6740
|
-
conditions.push(`#${name} >= :${valueName}Removal`);
|
|
6741
|
-
}
|
|
6742
|
-
}
|
|
6743
|
-
return { Update: {
|
|
6744
|
-
TableName: store.tableName,
|
|
6745
|
-
Key: {
|
|
6746
|
-
pk: "_hot-updater",
|
|
6747
|
-
sk: "limits.metadata"
|
|
6748
|
-
},
|
|
6749
|
-
UpdateExpression: `ADD ${updates.join(", ")}`,
|
|
6750
|
-
...conditions.length > 0 ? { ConditionExpression: conditions.join(" AND ") } : {},
|
|
6751
|
-
ExpressionAttributeNames: names,
|
|
6752
|
-
ExpressionAttributeValues: values
|
|
6753
|
-
} };
|
|
6754
|
-
};
|
|
6755
|
-
const updateBundleRelation = (store, bundleId, delta, ownedPatchDelta = 0) => {
|
|
6756
|
-
const names = {
|
|
6757
|
-
"#pk": "pk",
|
|
6758
|
-
"#version": "version"
|
|
6365
|
+
const binder = (map, prefix) => (entry) => {
|
|
6366
|
+
const token = `${prefix}${Object.keys(map).length}`;
|
|
6367
|
+
map[token] = entry;
|
|
6368
|
+
return token;
|
|
6759
6369
|
};
|
|
6760
|
-
const
|
|
6761
|
-
const
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6370
|
+
const name = binder(names, "#n");
|
|
6371
|
+
const value = binder(values, ":v");
|
|
6372
|
+
const { condition } = op;
|
|
6373
|
+
const guard = condition === void 0 ? void 0 : "v" in condition ? `${name("_v")} = ${value(condition.v)}` : `${condition.exists ? "attribute_exists" : "attribute_not_exists"}(pk)`;
|
|
6374
|
+
const item = (fields) => ({
|
|
6375
|
+
TableName: tableName,
|
|
6376
|
+
ConditionExpression: guard,
|
|
6377
|
+
...fields,
|
|
6378
|
+
ExpressionAttributeNames: Object.keys(names).length ? names : void 0,
|
|
6379
|
+
ExpressionAttributeValues: Object.keys(values).length ? values : void 0
|
|
6380
|
+
});
|
|
6381
|
+
const Key = {
|
|
6382
|
+
pk: op.key.pk,
|
|
6383
|
+
sk: op.key.sk
|
|
6384
|
+
};
|
|
6385
|
+
switch (op.type) {
|
|
6386
|
+
case "put": return { Put: item({ Item: toItem(op.key, op.value) }) };
|
|
6387
|
+
case "delete": return { Delete: item({ Key }) };
|
|
6388
|
+
case "check": return { ConditionCheck: item({
|
|
6389
|
+
Key,
|
|
6390
|
+
ConditionExpression: guard
|
|
6391
|
+
}) };
|
|
6392
|
+
case "add": {
|
|
6393
|
+
const init = op.init ?? {};
|
|
6394
|
+
return { Update: item({
|
|
6395
|
+
Key,
|
|
6396
|
+
UpdateExpression: `SET ${Object.keys({
|
|
6397
|
+
...init,
|
|
6398
|
+
...op.by
|
|
6399
|
+
}).map((column) => {
|
|
6400
|
+
const path = name(attribute(column));
|
|
6401
|
+
const by = op.by[column];
|
|
6402
|
+
if (by === void 0) return `${path} = if_not_exists(${path}, ${value(init[column])})`;
|
|
6403
|
+
return `${path} = if_not_exists(${path}, ${value(Number(init[column] ?? 0))}) + ${value(by)}`;
|
|
6404
|
+
}).join(", ")}`
|
|
6405
|
+
}) };
|
|
6780
6406
|
}
|
|
6781
6407
|
}
|
|
6782
|
-
if (additions.length > 0) update += ` ADD ${additions.join(", ")}`;
|
|
6783
|
-
return { Update: {
|
|
6784
|
-
TableName: store.tableName,
|
|
6785
|
-
Key: itemKey("bundles", bundleId),
|
|
6786
|
-
ConditionExpression: condition,
|
|
6787
|
-
ExpressionAttributeNames: names,
|
|
6788
|
-
ExpressionAttributeValues: values,
|
|
6789
|
-
UpdateExpression: update
|
|
6790
|
-
} };
|
|
6791
|
-
};
|
|
6792
|
-
const commitDynamoDBTransaction = async (store, actions) => {
|
|
6793
|
-
if (actions.length > 100) throw new DynamoDBTransactionLimitError(actions.length);
|
|
6794
|
-
await store.client.send(new _aws_sdk_lib_dynamodb.TransactWriteCommand({ TransactItems: [...actions] }));
|
|
6795
|
-
};
|
|
6796
|
-
const DYNAMODB_BATCH_GET_ATTEMPTS = 5;
|
|
6797
|
-
const DYNAMODB_BATCH_GET_BASE_DELAY_MS = 25;
|
|
6798
|
-
var DynamoDBBatchGetExhaustedError = class extends Error {
|
|
6799
|
-
name = "DynamoDBBatchGetExhaustedError";
|
|
6800
|
-
constructor(unprocessedKeyCount) {
|
|
6801
|
-
super(`DynamoDB did not process ${unprocessedKeyCount} batch-get keys after ${DYNAMODB_BATCH_GET_ATTEMPTS} attempts`);
|
|
6802
|
-
this.unprocessedKeyCount = unprocessedKeyCount;
|
|
6803
|
-
}
|
|
6804
6408
|
};
|
|
6805
|
-
|
|
6806
|
-
const
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
if (pending.length > 0) throw new DynamoDBBatchGetExhaustedError(pending.length);
|
|
6820
|
-
}
|
|
6821
|
-
return items;
|
|
6822
|
-
};
|
|
6823
|
-
const parseKeyReadItem = (item) => parseDynamoDBItem(boundedDynamoDBMetadataItem(item));
|
|
6824
|
-
const loadBundleItemsById = async (store, ids) => {
|
|
6825
|
-
const uniqueIds = [...new Set(ids)];
|
|
6826
|
-
if (uniqueIds.length === 0) return [];
|
|
6827
|
-
return (await batchGetDynamoDBItems(store, uniqueIds.map((id) => itemKey("bundles", id)))).map(parseKeyReadItem).filter((item) => item.pk === "bundles");
|
|
6828
|
-
};
|
|
6829
|
-
const loadPatchItemsById = async (store, ids) => {
|
|
6830
|
-
const uniqueIds = [...new Set(ids)];
|
|
6831
|
-
if (uniqueIds.length === 0) return [];
|
|
6832
|
-
return (await batchGetDynamoDBItems(store, uniqueIds.map((id) => itemKey("bundle_patches", id)))).map(parseKeyReadItem).filter((item) => item.pk === "bundle_patches");
|
|
6833
|
-
};
|
|
6834
|
-
const loadMetadataCount = async (store, model) => {
|
|
6835
|
-
if (model !== "bundles" && model !== "bundle_patches") return void 0;
|
|
6836
|
-
const field = model === "bundles" ? "bundles" : "patches";
|
|
6837
|
-
const { Item } = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
6838
|
-
TableName: store.tableName,
|
|
6839
|
-
Key: {
|
|
6840
|
-
pk: "_hot-updater",
|
|
6841
|
-
sk: "limits.metadata"
|
|
6842
|
-
},
|
|
6843
|
-
ConsistentRead: true,
|
|
6844
|
-
ProjectionExpression: "#count",
|
|
6845
|
-
ExpressionAttributeNames: { "#count": field }
|
|
6846
|
-
}));
|
|
6847
|
-
const count = Item?.[field];
|
|
6848
|
-
return typeof count === "number" && Number.isSafeInteger(count) && count >= 0 ? count : void 0;
|
|
6849
|
-
};
|
|
6850
|
-
var DynamoDBPatchIndexConsistencyError = class extends Error {
|
|
6851
|
-
name = "DynamoDBPatchIndexConsistencyError";
|
|
6852
|
-
constructor(bundleId, expectedCount) {
|
|
6853
|
-
super(`DynamoDB patch index did not converge to ${expectedCount} rows for bundle "${bundleId}"`);
|
|
6854
|
-
this.bundleId = bundleId;
|
|
6855
|
-
this.expectedCount = expectedCount;
|
|
6856
|
-
}
|
|
6857
|
-
};
|
|
6858
|
-
const waitForIndex = (attempt) => new Promise((resolve) => setTimeout(resolve, 20 * (attempt + 1)));
|
|
6859
|
-
const queryCompleteOwnerPatches = async (store, indexName, bundleId) => {
|
|
6860
|
-
const owner = await loadBundleItem(store, bundleId);
|
|
6861
|
-
if (!owner || owner.owned_patch_count === 0) return [];
|
|
6862
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
6863
|
-
const patchIds = await queryOwnerPatchIds(store, indexName, bundleId);
|
|
6864
|
-
const patchIdSet = new Set(patchIds);
|
|
6865
|
-
const patches = (await loadPatchItemsById(store, patchIds)).filter(({ row }) => row.bundle_id === bundleId);
|
|
6866
|
-
if (patchIds.length === owner.owned_patch_count && patchIdSet.size === owner.owned_patch_count && patches.length === owner.owned_patch_count && patches.every(({ sk }) => patchIdSet.has(sk))) return patches.map(({ row }) => row).sort((left, right) => left.order_index - right.order_index || left.id.localeCompare(right.id));
|
|
6867
|
-
if (attempt < 2) await waitForIndex(attempt);
|
|
6868
|
-
}
|
|
6869
|
-
throw new DynamoDBPatchIndexConsistencyError(bundleId, owner.owned_patch_count);
|
|
6870
|
-
};
|
|
6871
|
-
const queryCompleteOwnersPatches = async (store, indexName, bundleIds) => {
|
|
6872
|
-
const owners = (await loadBundleItemsById(store, bundleIds)).filter(({ owned_patch_count }) => owned_patch_count > 0);
|
|
6873
|
-
const firstOwner = owners[0];
|
|
6874
|
-
if (!firstOwner) return [];
|
|
6875
|
-
let failedOwner = firstOwner;
|
|
6876
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
6877
|
-
const candidateIdsByOwner = await Promise.all(owners.map(({ sk }) => queryOwnerPatchIds(store, indexName, sk)));
|
|
6878
|
-
const patches = await loadPatchItemsById(store, candidateIdsByOwner.flat());
|
|
6879
|
-
const patchesById = new Map(patches.map((patch) => [patch.sk, patch]));
|
|
6880
|
-
const mismatchedOwner = owners.find((owner, ownerIndex) => {
|
|
6881
|
-
const ids = candidateIdsByOwner[ownerIndex] ?? [];
|
|
6882
|
-
const uniqueIds = new Set(ids);
|
|
6883
|
-
return ids.length !== owner.owned_patch_count || uniqueIds.size !== owner.owned_patch_count || ids.some((id) => patchesById.get(id)?.row.bundle_id !== owner.sk);
|
|
6884
|
-
});
|
|
6885
|
-
if (!mismatchedOwner) return patches.map(({ row }) => row);
|
|
6886
|
-
failedOwner = mismatchedOwner;
|
|
6887
|
-
if (attempt < 2) await waitForIndex(attempt);
|
|
6888
|
-
}
|
|
6889
|
-
throw new DynamoDBPatchIndexConsistencyError(failedOwner.sk, failedOwner.owned_patch_count);
|
|
6890
|
-
};
|
|
6891
|
-
const createDynamoDBBundlePatchTable = (store, indexName) => ({ findByBundleIds: (bundleIds) => bundleIds.length === 0 ? Promise.resolve([]) : queryCompleteOwnersPatches(store, indexName, bundleIds) });
|
|
6892
|
-
const compare = (left, right) => {
|
|
6893
|
-
if (typeof left === "number" && typeof right === "number") return left - right;
|
|
6894
|
-
if (left == null) return right == null ? 0 : -1;
|
|
6895
|
-
if (right == null) return 1;
|
|
6896
|
-
return JSON.stringify(left).localeCompare(JSON.stringify(right));
|
|
6897
|
-
};
|
|
6898
|
-
const compareString = (actual, expected, mode, predicate) => {
|
|
6899
|
-
if (typeof actual !== "string") return false;
|
|
6900
|
-
return mode === "insensitive" ? predicate(actual.toLocaleLowerCase(), expected.toLocaleLowerCase()) : predicate(actual, expected);
|
|
6901
|
-
};
|
|
6902
|
-
const matchesCondition = (row, condition) => {
|
|
6903
|
-
const actual = Reflect.get(row, condition.field);
|
|
6904
|
-
const expected = Reflect.get(condition, "value");
|
|
6905
|
-
switch (Reflect.get(condition, "operator") ?? "eq") {
|
|
6906
|
-
case "eq": return typeof expected === "string" ? compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value === query) : actual === expected;
|
|
6907
|
-
case "ne":
|
|
6908
|
-
if (actual == null) return false;
|
|
6909
|
-
return typeof expected === "string" ? !compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value === query) : actual !== expected;
|
|
6910
|
-
case "gt": return actual != null && compare(actual, expected) > 0;
|
|
6911
|
-
case "gte": return actual != null && compare(actual, expected) >= 0;
|
|
6912
|
-
case "lt": return actual != null && compare(actual, expected) < 0;
|
|
6913
|
-
case "lte": return actual != null && compare(actual, expected) <= 0;
|
|
6914
|
-
case "in": return Array.isArray(expected) && expected.some((candidate) => candidate === actual);
|
|
6915
|
-
case "not_in": return Array.isArray(expected) && (expected.length === 0 || actual != null && expected.every((candidate) => candidate !== actual));
|
|
6916
|
-
case "contains": return typeof expected === "string" ? compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value.includes(query)) : false;
|
|
6917
|
-
case "starts_with": return typeof expected === "string" ? compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value.startsWith(query)) : false;
|
|
6918
|
-
case "ends_with": return typeof expected === "string" ? compareString(actual, expected, Reflect.get(condition, "mode"), (value, query) => value.endsWith(query)) : false;
|
|
6919
|
-
default: return false;
|
|
6920
|
-
}
|
|
6921
|
-
};
|
|
6922
|
-
const matchesDynamoDBWhere = (row, where) => {
|
|
6923
|
-
if (!where || where.length === 0) return true;
|
|
6924
|
-
let result = matchesCondition(row, where[0]);
|
|
6925
|
-
for (const condition of where.slice(1)) {
|
|
6926
|
-
const current = matchesCondition(row, condition);
|
|
6927
|
-
result = condition.connector === "OR" ? result || current : result && current;
|
|
6928
|
-
}
|
|
6929
|
-
return result;
|
|
6930
|
-
};
|
|
6931
|
-
const compareRows = (left, right, orderBy) => {
|
|
6932
|
-
for (const clause of orderBy) {
|
|
6933
|
-
const leftValue = Reflect.get(left, clause.field);
|
|
6934
|
-
const rightValue = Reflect.get(right, clause.field);
|
|
6935
|
-
if (leftValue == null || rightValue == null) {
|
|
6936
|
-
if (leftValue == null && rightValue == null) continue;
|
|
6937
|
-
const nulls = clause.nulls ?? (clause.direction === "asc" ? "last" : "first");
|
|
6938
|
-
const order = leftValue == null ? -1 : 1;
|
|
6939
|
-
return nulls === "first" ? order : -order;
|
|
6940
|
-
}
|
|
6941
|
-
const order = compare(leftValue, rightValue);
|
|
6942
|
-
if (order !== 0) return clause.direction === "asc" ? order : -order;
|
|
6943
|
-
}
|
|
6944
|
-
return 0;
|
|
6945
|
-
};
|
|
6946
|
-
const distinctKey = (row, distinctOn) => JSON.stringify(distinctOn.fields.map((field) => Reflect.get(row, field)));
|
|
6947
|
-
const queryDynamoDBRows = (rows, input) => {
|
|
6948
|
-
const filtered = rows.filter((row) => matchesDynamoDBWhere(row, input.where));
|
|
6949
|
-
const orderBy = input.orderBy;
|
|
6950
|
-
const ordered = orderBy ? filtered.toSorted((left, right) => compareRows(left, right, orderBy)) : filtered;
|
|
6951
|
-
const distinctOn = input.distinctOn;
|
|
6952
|
-
const distinct = distinctOn ? (() => {
|
|
6953
|
-
const seen = /* @__PURE__ */ new Set();
|
|
6954
|
-
return ordered.filter((row) => {
|
|
6955
|
-
const key = distinctKey(row, distinctOn);
|
|
6956
|
-
if (seen.has(key)) return false;
|
|
6957
|
-
seen.add(key);
|
|
6958
|
-
return true;
|
|
6959
|
-
});
|
|
6960
|
-
})() : ordered;
|
|
6961
|
-
const offset = input.offset ?? 0;
|
|
6962
|
-
return distinct.slice(offset, offset + (input.limit ?? 100));
|
|
6963
|
-
};
|
|
6964
|
-
const countDistinctDynamoDBRows = (rows, fields) => {
|
|
6965
|
-
if (fields === void 0) return rows.length;
|
|
6966
|
-
return new Set(rows.map((row) => JSON.stringify(fields.map((field) => Reflect.get(row, field))))).size;
|
|
6967
|
-
};
|
|
6968
|
-
const createDynamoDBBundle = async (store, row) => {
|
|
6969
|
-
const counter = metadataUpdate(store, { bundles: 1 });
|
|
6970
|
-
if (!counter) return;
|
|
6971
|
-
await commitDynamoDBTransaction(store, [counter, { Put: {
|
|
6972
|
-
TableName: store.tableName,
|
|
6973
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBBundleItem(row)),
|
|
6974
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
6975
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
6976
|
-
} }]);
|
|
6977
|
-
};
|
|
6978
|
-
const createDynamoDBPatch = async (store, row) => {
|
|
6979
|
-
const bundleIds = [...new Set([row.bundle_id, row.base_bundle_id])];
|
|
6980
|
-
const counter = metadataUpdate(store, { bundle_patches: 1 });
|
|
6981
|
-
if (!counter) return;
|
|
6982
|
-
await commitDynamoDBTransaction(store, [
|
|
6983
|
-
counter,
|
|
6984
|
-
...bundleIds.map((bundleId) => updateBundleRelation(store, bundleId, 1, bundleId === row.bundle_id ? 1 : 0)),
|
|
6985
|
-
{ Put: {
|
|
6986
|
-
TableName: store.tableName,
|
|
6987
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBPatchItem(row)),
|
|
6988
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
6989
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
6990
|
-
} }
|
|
6991
|
-
]);
|
|
6992
|
-
};
|
|
6993
|
-
const replaceDynamoDBBundle = async (store, current, row) => {
|
|
6994
|
-
await store.client.send(new _aws_sdk_lib_dynamodb.PutCommand({
|
|
6995
|
-
TableName: store.tableName,
|
|
6996
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBBundleItem(row, current.version + 1, current.relation_count, current.owned_patch_count)),
|
|
6997
|
-
ConditionExpression: "#version = :currentVersion",
|
|
6998
|
-
ExpressionAttributeNames: { "#version": "version" },
|
|
6999
|
-
ExpressionAttributeValues: { ":currentVersion": current.version }
|
|
7000
|
-
}));
|
|
7001
|
-
};
|
|
7002
|
-
const deleteAction = (store, item) => ({ Delete: {
|
|
7003
|
-
TableName: store.tableName,
|
|
7004
|
-
Key: {
|
|
7005
|
-
pk: item.pk,
|
|
7006
|
-
sk: item.sk
|
|
7007
|
-
},
|
|
7008
|
-
ConditionExpression: "#version = :currentVersion",
|
|
7009
|
-
ExpressionAttributeNames: { "#version": "version" },
|
|
7010
|
-
ExpressionAttributeValues: { ":currentVersion": item.version }
|
|
7011
|
-
} });
|
|
7012
|
-
const deleteDynamoDBPatch = async (store, item) => {
|
|
7013
|
-
const counter = metadataUpdate(store, { bundle_patches: -1 });
|
|
7014
|
-
if (!counter) return;
|
|
7015
|
-
await commitDynamoDBTransaction(store, [
|
|
7016
|
-
counter,
|
|
7017
|
-
...[...new Set([item.row.bundle_id, item.row.base_bundle_id])].map((id) => updateBundleRelation(store, id, -1, id === item.row.bundle_id ? -1 : 0)),
|
|
7018
|
-
deleteAction(store, item)
|
|
7019
|
-
]);
|
|
7020
|
-
};
|
|
7021
|
-
const deleteBundle = async (store, bundle, patches) => {
|
|
7022
|
-
const counter = metadataUpdate(store, {
|
|
7023
|
-
bundles: -1,
|
|
7024
|
-
bundle_patches: -patches.length
|
|
7025
|
-
});
|
|
7026
|
-
if (!counter) return;
|
|
7027
|
-
const otherEndpointCounts = /* @__PURE__ */ new Map();
|
|
7028
|
-
for (const { row } of patches) for (const id of new Set([row.bundle_id, row.base_bundle_id])) {
|
|
7029
|
-
if (id === bundle.sk) continue;
|
|
7030
|
-
const current = otherEndpointCounts.get(id) ?? {
|
|
7031
|
-
owned: 0,
|
|
7032
|
-
relations: 0
|
|
7033
|
-
};
|
|
7034
|
-
otherEndpointCounts.set(id, {
|
|
7035
|
-
owned: current.owned + (id === row.bundle_id ? 1 : 0),
|
|
7036
|
-
relations: current.relations + 1
|
|
7037
|
-
});
|
|
7038
|
-
}
|
|
7039
|
-
await commitDynamoDBTransaction(store, [
|
|
7040
|
-
counter,
|
|
7041
|
-
...[...otherEndpointCounts].map(([id, count]) => updateBundleRelation(store, id, -count.relations, -count.owned)),
|
|
7042
|
-
...patches.map((patch) => deleteAction(store, patch)),
|
|
7043
|
-
deleteAction(store, bundle)
|
|
7044
|
-
]);
|
|
7045
|
-
};
|
|
7046
|
-
const deleteDynamoDBBundles = async (store, bundles, allPatches) => {
|
|
7047
|
-
let patches = [...allPatches];
|
|
7048
|
-
const versionIncrements = /* @__PURE__ */ new Map();
|
|
7049
|
-
for (const originalBundle of bundles) {
|
|
7050
|
-
const bundle = {
|
|
7051
|
-
...originalBundle,
|
|
7052
|
-
version: originalBundle.version + (versionIncrements.get(originalBundle.sk) ?? 0)
|
|
7053
|
-
};
|
|
7054
|
-
const related = patches.filter(({ row }) => row.bundle_id === bundle.sk || row.base_bundle_id === bundle.sk);
|
|
7055
|
-
await deleteBundle(store, bundle, related);
|
|
7056
|
-
const updatedBundleIds = new Set(related.flatMap(({ row }) => [row.bundle_id, row.base_bundle_id]));
|
|
7057
|
-
updatedBundleIds.delete(bundle.sk);
|
|
7058
|
-
for (const id of updatedBundleIds) versionIncrements.set(id, (versionIncrements.get(id) ?? 0) + 1);
|
|
7059
|
-
const removedPatchIds = new Set(related.map(({ sk }) => sk));
|
|
7060
|
-
patches = patches.filter(({ sk }) => !removedPatchIds.has(sk));
|
|
7061
|
-
}
|
|
6409
|
+
/** Errors, and cancellation reasons, that a retry of the whole write can clear. */
|
|
6410
|
+
const TRANSIENT = /* @__PURE__ */ new Set([
|
|
6411
|
+
"TransactionConflict",
|
|
6412
|
+
"ThrottlingError",
|
|
6413
|
+
"ProvisionedThroughputExceeded",
|
|
6414
|
+
"RequestLimitExceeded",
|
|
6415
|
+
"TransactionInProgressException",
|
|
6416
|
+
"ProvisionedThroughputExceededException",
|
|
6417
|
+
"ThrottlingException",
|
|
6418
|
+
"InternalServerError"
|
|
6419
|
+
]);
|
|
6420
|
+
/** A missing table reads as one to the schema fence, like SQL's 42P01. */
|
|
6421
|
+
const missingTable = (error) => {
|
|
6422
|
+
throw nameOf(error) === "ResourceNotFoundException" ? Object.assign(new Error(error.message, { cause: error }), { code: "42P01" }) : error;
|
|
7062
6423
|
};
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
}
|
|
7069
|
-
const
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
}
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
switch (input.model) {
|
|
7081
|
-
case "bundles":
|
|
7082
|
-
await createDynamoDBBundle(store, input.data);
|
|
7083
|
-
return input.data;
|
|
7084
|
-
case "bundle_patches":
|
|
7085
|
-
await createDynamoDBPatch(store, input.data);
|
|
7086
|
-
return input.data;
|
|
7087
|
-
case "releases": {
|
|
7088
|
-
const channel = await loadChannelItem(store, input.data.channel_id);
|
|
7089
|
-
if (channel === void 0) throw new DynamoDBStoredItemError();
|
|
7090
|
-
await commitDynamoDBTransaction(store, [
|
|
7091
|
-
updateChannelReferenceCount(store, channel, 1),
|
|
7092
|
-
{ Put: {
|
|
7093
|
-
TableName: store.tableName,
|
|
7094
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBReleaseItem(input.data)),
|
|
7095
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7096
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7097
|
-
} },
|
|
7098
|
-
{ Put: {
|
|
7099
|
-
TableName: store.tableName,
|
|
7100
|
-
Item: releaseLocatorItem(input.data),
|
|
7101
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7102
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7103
|
-
} }
|
|
7104
|
-
]);
|
|
7105
|
-
return input.data;
|
|
7106
|
-
}
|
|
7107
|
-
case "release_catalogs":
|
|
7108
|
-
await store.client.send(new _aws_sdk_lib_dynamodb.PutCommand({
|
|
7109
|
-
TableName: store.tableName,
|
|
7110
|
-
Item: boundedDynamoDBCatalogItem(toDynamoDBReleaseCatalogItem(input.data)),
|
|
7111
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7112
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
6424
|
+
/**
|
|
6425
|
+
* The key-value store over one DynamoDB table keyed by `pk` and `sk`, with
|
|
6426
|
+
* no secondary index. Reads are strongly consistent; a write is one
|
|
6427
|
+
* `TransactWriteItems` with a client request token.
|
|
6428
|
+
*/
|
|
6429
|
+
const createDynamoDBStore = ({ client, tableName, nativePageSize }) => {
|
|
6430
|
+
const documents = _aws_sdk_lib_dynamodb.DynamoDBDocumentClient.from(client, { marshallOptions: { removeUndefinedValues: true } });
|
|
6431
|
+
return {
|
|
6432
|
+
id: "dynamoDB",
|
|
6433
|
+
limits: DYNAMODB_LIMITS,
|
|
6434
|
+
async get(keys) {
|
|
6435
|
+
const found = /* @__PURE__ */ new Map();
|
|
6436
|
+
const unique = [...new Map(keys.map((key) => [idOf(key), key])).values()];
|
|
6437
|
+
for (let at = 0; at < unique.length; at += GET_BATCH) {
|
|
6438
|
+
let pending = unique.slice(at, at + GET_BATCH).map(({ pk, sk }) => ({
|
|
6439
|
+
pk,
|
|
6440
|
+
sk
|
|
7113
6441
|
}));
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
async update(input) {
|
|
7123
|
-
if (input.model === "releases") {
|
|
7124
|
-
const id = exactDynamoDBId(input.where);
|
|
7125
|
-
if (id === void 0) throw new DynamoDBUnsupportedModelError();
|
|
7126
|
-
const current = await loadReleaseItem(store, id);
|
|
7127
|
-
if (current === void 0) return null;
|
|
7128
|
-
const updated = {
|
|
7129
|
-
...current.row,
|
|
7130
|
-
...input.update
|
|
7131
|
-
};
|
|
7132
|
-
const nextPartition = releaseScopePartition(updated.scope_key);
|
|
7133
|
-
const moved = current.pk !== nextPartition;
|
|
7134
|
-
await commitDynamoDBTransaction(store, [
|
|
7135
|
-
...moved ? [{ Delete: {
|
|
7136
|
-
TableName: store.tableName,
|
|
7137
|
-
Key: {
|
|
7138
|
-
pk: current.pk,
|
|
7139
|
-
sk: current.sk
|
|
7140
|
-
},
|
|
7141
|
-
ConditionExpression: "#version = :version",
|
|
7142
|
-
ExpressionAttributeNames: { "#version": "version" },
|
|
7143
|
-
ExpressionAttributeValues: { ":version": current.version }
|
|
7144
|
-
} }] : [],
|
|
7145
|
-
{ Put: {
|
|
7146
|
-
TableName: store.tableName,
|
|
7147
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBReleaseItem(updated, current.version + 1)),
|
|
7148
|
-
ConditionExpression: moved ? "attribute_not_exists(#pk)" : "#version = :version",
|
|
7149
|
-
ExpressionAttributeNames: moved ? { "#pk": "pk" } : { "#version": "version" },
|
|
7150
|
-
...moved ? {} : { ExpressionAttributeValues: { ":version": current.version } }
|
|
7151
|
-
} },
|
|
7152
|
-
{ Put: {
|
|
7153
|
-
TableName: store.tableName,
|
|
7154
|
-
Item: releaseLocatorItem(updated),
|
|
7155
|
-
ConditionExpression: "#scopeKey = :scopeKey",
|
|
7156
|
-
ExpressionAttributeNames: { "#scopeKey": "scope_key" },
|
|
7157
|
-
ExpressionAttributeValues: { ":scopeKey": current.row.scope_key }
|
|
7158
|
-
} }
|
|
7159
|
-
]);
|
|
7160
|
-
return updated;
|
|
7161
|
-
}
|
|
7162
|
-
if (input.model === "release_catalogs") {
|
|
7163
|
-
const scopeKey = exactDynamoDBField(input.where, "scope_key");
|
|
7164
|
-
if (scopeKey === void 0) throw new DynamoDBUnsupportedModelError();
|
|
7165
|
-
const current = await loadReleaseCatalogItem(store, scopeKey);
|
|
7166
|
-
if (current === void 0) return null;
|
|
7167
|
-
const updated = {
|
|
7168
|
-
...current.row,
|
|
7169
|
-
...input.update
|
|
7170
|
-
};
|
|
7171
|
-
await store.client.send(new _aws_sdk_lib_dynamodb.PutCommand({
|
|
7172
|
-
TableName: store.tableName,
|
|
7173
|
-
Item: boundedDynamoDBCatalogItem(toDynamoDBReleaseCatalogItem(updated, current.version + 1)),
|
|
7174
|
-
ConditionExpression: "#version = :version",
|
|
7175
|
-
ExpressionAttributeNames: { "#version": "version" },
|
|
7176
|
-
ExpressionAttributeValues: { ":version": current.version }
|
|
7177
|
-
}));
|
|
7178
|
-
return updated;
|
|
7179
|
-
}
|
|
7180
|
-
if (input.model !== "bundles") throw new DynamoDBUnsupportedModelError();
|
|
7181
|
-
const id = exactDynamoDBId(input.where);
|
|
7182
|
-
const current = id === void 0 ? (await loadBundleItems(store)).find(({ row }) => matchesDynamoDBWhere(row, input.where)) : await loadBundleItem(store, id);
|
|
7183
|
-
if (!current) return null;
|
|
7184
|
-
const updated = {
|
|
7185
|
-
...current.row,
|
|
7186
|
-
...input.update
|
|
7187
|
-
};
|
|
7188
|
-
await replaceDynamoDBBundle(store, current, updated);
|
|
7189
|
-
return updated;
|
|
7190
|
-
},
|
|
7191
|
-
async delete(input) {
|
|
7192
|
-
if (input.model === "channels") {
|
|
7193
|
-
const id = exactDynamoDBId(input.where);
|
|
7194
|
-
if (id !== void 0) await deleteDynamoDBChannel(store, { id });
|
|
7195
|
-
return;
|
|
7196
|
-
}
|
|
7197
|
-
if (input.model === "bundle_patches") {
|
|
7198
|
-
const items = (await loadPatchItems(store)).filter(({ row }) => matchesDynamoDBWhere(row, input.where));
|
|
7199
|
-
for (const item of items) await deleteDynamoDBPatch(store, item);
|
|
7200
|
-
return;
|
|
7201
|
-
}
|
|
7202
|
-
if (input.model === "releases") {
|
|
7203
|
-
const items = (await loadReleaseItems(store)).filter(({ row }) => matchesDynamoDBWhere(row, input.where));
|
|
7204
|
-
for (const item of items) {
|
|
7205
|
-
const channel = await loadChannelItem(store, item.row.channel_id);
|
|
7206
|
-
if (channel === void 0) throw new DynamoDBStoredItemError();
|
|
7207
|
-
await commitDynamoDBTransaction(store, [
|
|
7208
|
-
updateChannelReferenceCount(store, channel, -1),
|
|
7209
|
-
deleteAction(store, item),
|
|
7210
|
-
{ Delete: {
|
|
7211
|
-
TableName: store.tableName,
|
|
7212
|
-
Key: {
|
|
7213
|
-
pk: DYNAMODB_RELEASE_ID_PARTITION,
|
|
7214
|
-
sk: item.sk
|
|
7215
|
-
},
|
|
7216
|
-
ConditionExpression: "#scopeKey = :scopeKey",
|
|
7217
|
-
ExpressionAttributeNames: { "#scopeKey": "scope_key" },
|
|
7218
|
-
ExpressionAttributeValues: { ":scopeKey": item.row.scope_key }
|
|
7219
|
-
} }
|
|
7220
|
-
]);
|
|
7221
|
-
}
|
|
7222
|
-
return;
|
|
7223
|
-
}
|
|
7224
|
-
if (input.model !== "bundles") throw new DynamoDBUnsupportedModelError();
|
|
7225
|
-
const bundleItems = (await loadBundleItems(store)).filter(({ row }) => matchesDynamoDBWhere(row, input.where));
|
|
7226
|
-
if (bundleItems.length === 0) return;
|
|
7227
|
-
await deleteDynamoDBBundles(store, bundleItems, await loadPatchItems(store));
|
|
7228
|
-
},
|
|
7229
|
-
async count(input) {
|
|
7230
|
-
if ((input.where === void 0 || input.where.length === 0) && input.distinct === void 0) {
|
|
7231
|
-
const count = await loadMetadataCount(store, input.model);
|
|
7232
|
-
if (count !== void 0) return count;
|
|
7233
|
-
}
|
|
7234
|
-
switch (input.model) {
|
|
7235
|
-
case "bundles": return countDistinctDynamoDBRows((await loadBundleItems(store)).map(({ row }) => row).filter((row) => matchesDynamoDBWhere(row, input.where)), distinctFields(input.distinct));
|
|
7236
|
-
case "bundle_patches": {
|
|
7237
|
-
const ownerId = exactDynamoDBPatchOwner(input.where);
|
|
7238
|
-
return countDistinctDynamoDBRows((ownerId ? await queryCompleteOwnerPatches(store, updateIndexName, ownerId) : (await loadPatchItems(store)).map(({ row }) => row)).filter((row) => matchesDynamoDBWhere(row, input.where)), distinctFields(input.distinct));
|
|
7239
|
-
}
|
|
7240
|
-
case "releases": {
|
|
7241
|
-
const scopeKey = exactDynamoDBField(input.where, "scope_key");
|
|
7242
|
-
return countDistinctDynamoDBRows((scopeKey === void 0 ? await loadReleaseItems(store) : await loadReleaseItemsByScope(store, scopeKey)).map(({ row }) => row).filter((row) => matchesDynamoDBWhere(row, input.where)), distinctFields(input.distinct));
|
|
7243
|
-
}
|
|
7244
|
-
}
|
|
7245
|
-
throw new DynamoDBUnsupportedModelError();
|
|
7246
|
-
},
|
|
7247
|
-
async findOne(input) {
|
|
7248
|
-
const id = exactDynamoDBId(input.where);
|
|
7249
|
-
switch (input.model) {
|
|
7250
|
-
case "bundles":
|
|
7251
|
-
if (id !== void 0) return (await loadBundleItem(store, id))?.row ?? null;
|
|
7252
|
-
return (await loadBundleItems(store)).map(({ row }) => row).find((row) => matchesDynamoDBWhere(row, input.where)) ?? null;
|
|
7253
|
-
case "bundle_patches":
|
|
7254
|
-
if (id !== void 0) return (await loadPatchItem(store, id))?.row ?? null;
|
|
7255
|
-
{
|
|
7256
|
-
const ownerId = exactDynamoDBPatchOwner(input.where);
|
|
7257
|
-
if (ownerId === void 0) break;
|
|
7258
|
-
return (await queryCompleteOwnerPatches(store, updateIndexName, ownerId)).find((row) => matchesDynamoDBWhere(row, input.where)) ?? null;
|
|
7259
|
-
}
|
|
7260
|
-
break;
|
|
7261
|
-
case "channels":
|
|
7262
|
-
if (id !== void 0) return (await loadChannelItem(store, id))?.row ?? null;
|
|
7263
|
-
return (await loadChannelItems(store)).map(({ row }) => row).find((row) => matchesDynamoDBWhere(row, input.where)) ?? null;
|
|
7264
|
-
case "releases":
|
|
7265
|
-
if (id !== void 0) return (await loadReleaseItem(store, id))?.row ?? null;
|
|
7266
|
-
{
|
|
7267
|
-
const scopeKey = exactDynamoDBField(input.where, "scope_key");
|
|
7268
|
-
return (scopeKey === void 0 ? await loadReleaseItems(store) : await loadReleaseItemsByScope(store, scopeKey)).map(({ row }) => row).find((row) => matchesDynamoDBWhere(row, input.where)) ?? null;
|
|
7269
|
-
}
|
|
7270
|
-
case "release_catalogs": {
|
|
7271
|
-
const scopeKey = exactDynamoDBField(input.where, "scope_key");
|
|
7272
|
-
if (scopeKey !== void 0) return (await loadReleaseCatalogItem(store, scopeKey))?.row ?? null;
|
|
7273
|
-
return (await loadReleaseCatalogItems(store)).map(({ row }) => row).find((row) => matchesDynamoDBWhere(row, input.where)) ?? null;
|
|
7274
|
-
}
|
|
7275
|
-
}
|
|
7276
|
-
if (input.model === "bundle_patches") return (await loadPatchItems(store)).map(({ row }) => row).find((row) => matchesDynamoDBWhere(row, input.where)) ?? null;
|
|
7277
|
-
throw new DynamoDBUnsupportedModelError();
|
|
7278
|
-
},
|
|
7279
|
-
async findMany(input) {
|
|
7280
|
-
if (input.limit === 0) return [];
|
|
7281
|
-
switch (input.model) {
|
|
7282
|
-
case "bundles": {
|
|
7283
|
-
const ids = exactDynamoDBBundleIds(input.where);
|
|
7284
|
-
if (ids !== void 0) return queryDynamoDBRows((await loadBundleItemsById(store, ids)).map(({ row }) => row), input);
|
|
7285
|
-
const orderBy = input.orderBy;
|
|
7286
|
-
const direction = orderBy?.[0]?.direction;
|
|
7287
|
-
if ((input.offset ?? 0) === 0 && input.distinctOn === void 0 && orderBy?.length === 1 && orderBy[0]?.field === "id" && (direction === "asc" || direction === "desc")) return (await queryBundleItemsPage(store, {
|
|
7288
|
-
direction,
|
|
7289
|
-
limit: input.limit ?? 100,
|
|
7290
|
-
matches: (row) => matchesDynamoDBWhere(row, input.where),
|
|
7291
|
-
start: dynamoDBBundlePageStart(input.where, direction)
|
|
7292
|
-
})).map(({ row }) => row);
|
|
7293
|
-
return queryDynamoDBRows((await loadBundleItems(store)).map(({ row }) => row), input);
|
|
7294
|
-
}
|
|
7295
|
-
case "bundle_patches": {
|
|
7296
|
-
const ownerIds = exactDynamoDBPatchOwners(input.where);
|
|
7297
|
-
if (ownerIds !== void 0) {
|
|
7298
|
-
const ownerId = ownerIds.length === 1 ? ownerIds[0] : void 0;
|
|
7299
|
-
return queryDynamoDBRows(ownerId !== void 0 ? await queryCompleteOwnerPatches(store, updateIndexName, ownerId) : await queryCompleteOwnersPatches(store, updateIndexName, ownerIds), input);
|
|
6442
|
+
for (let round = 0; pending.length > 0; round += 1) {
|
|
6443
|
+
if (round > 0) await sleep(2 ** round * 10);
|
|
6444
|
+
const result = await documents.send(new _aws_sdk_lib_dynamodb.BatchGetCommand({ RequestItems: { [tableName]: {
|
|
6445
|
+
Keys: pending,
|
|
6446
|
+
ConsistentRead: true
|
|
6447
|
+
} } })).catch(missingTable);
|
|
6448
|
+
for (const item of result.Responses?.[tableName] ?? []) found.set(idOf(item), toRow(item));
|
|
6449
|
+
pending = result.UnprocessedKeys?.[tableName]?.Keys ?? [];
|
|
7300
6450
|
}
|
|
7301
|
-
return queryDynamoDBRows((await loadPatchItems(store)).map(({ row }) => row), input);
|
|
7302
|
-
}
|
|
7303
|
-
case "channels": return queryDynamoDBRows((await loadChannelItems(store)).map(({ row }) => row), input);
|
|
7304
|
-
case "releases": {
|
|
7305
|
-
const scopeKey = exactDynamoDBField(input.where, "scope_key");
|
|
7306
|
-
return queryDynamoDBRows((scopeKey === void 0 ? await loadReleaseItems(store) : await loadReleaseItemsByScope(store, scopeKey)).map(({ row }) => row), input);
|
|
7307
6451
|
}
|
|
7308
|
-
|
|
7309
|
-
}
|
|
7310
|
-
throw new DynamoDBUnsupportedModelError();
|
|
7311
|
-
},
|
|
7312
|
-
insertChannel: (input) => insertDynamoDBChannel(store, input),
|
|
7313
|
-
deleteChannel: (input) => deleteDynamoDBChannel(store, input)
|
|
7314
|
-
});
|
|
7315
|
-
var DynamoDBDuplicatePatchError = class extends Error {
|
|
7316
|
-
name = "DynamoDBDuplicatePatchError";
|
|
7317
|
-
constructor(patchId) {
|
|
7318
|
-
super(`DynamoDB bundle mutation contains duplicate patch "${patchId}"`);
|
|
7319
|
-
this.patchId = patchId;
|
|
7320
|
-
}
|
|
7321
|
-
};
|
|
7322
|
-
const putNewBundle = (store, row, relationCount, ownedPatchCount = relationCount) => ({ Put: {
|
|
7323
|
-
TableName: store.tableName,
|
|
7324
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBBundleItem(row, 1, relationCount, ownedPatchCount)),
|
|
7325
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7326
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7327
|
-
} });
|
|
7328
|
-
const putUpdatedBundle = (store, current, row, relationCount, ownedPatchCount) => ({ Put: {
|
|
7329
|
-
TableName: store.tableName,
|
|
7330
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBBundleItem(row, current.version + 1, relationCount, ownedPatchCount)),
|
|
7331
|
-
ConditionExpression: "#version = :currentVersion",
|
|
7332
|
-
ExpressionAttributeNames: { "#version": "version" },
|
|
7333
|
-
ExpressionAttributeValues: { ":currentVersion": current.version }
|
|
7334
|
-
} });
|
|
7335
|
-
const putPatch = (store, row, current) => ({ Put: {
|
|
7336
|
-
TableName: store.tableName,
|
|
7337
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBPatchItem(row, (current?.version ?? 0) + 1)),
|
|
7338
|
-
ConditionExpression: current ? "#version = :currentVersion" : "attribute_not_exists(#pk)",
|
|
7339
|
-
ExpressionAttributeNames: current ? { "#version": "version" } : { "#pk": "pk" },
|
|
7340
|
-
...current ? { ExpressionAttributeValues: { ":currentVersion": current.version } } : {}
|
|
7341
|
-
} });
|
|
7342
|
-
const deletePatch = (store, item) => ({ Delete: {
|
|
7343
|
-
TableName: store.tableName,
|
|
7344
|
-
Key: itemKey(item.pk, item.sk),
|
|
7345
|
-
ConditionExpression: "#version = :currentVersion",
|
|
7346
|
-
ExpressionAttributeNames: { "#version": "version" },
|
|
7347
|
-
ExpressionAttributeValues: { ":currentVersion": item.version }
|
|
7348
|
-
} });
|
|
7349
|
-
var DynamoDBCommitStateError = class extends Error {
|
|
7350
|
-
name = "DynamoDBCommitStateError";
|
|
7351
|
-
constructor(message) {
|
|
7352
|
-
super(message);
|
|
7353
|
-
}
|
|
7354
|
-
};
|
|
7355
|
-
const rowsEqual = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
7356
|
-
const putChannel = (store, row, current, referenceCount) => ({ Put: {
|
|
7357
|
-
TableName: store.tableName,
|
|
7358
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBChannelItem(row, (current?.version ?? 0) + 1, referenceCount)),
|
|
7359
|
-
ConditionExpression: current ? "#version = :version" : "attribute_not_exists(#pk)",
|
|
7360
|
-
ExpressionAttributeNames: current ? { "#version": "version" } : { "#pk": "pk" },
|
|
7361
|
-
...current ? { ExpressionAttributeValues: { ":version": current.version } } : {}
|
|
7362
|
-
} });
|
|
7363
|
-
const deleteChannelActions = (store, current) => [{ Delete: {
|
|
7364
|
-
TableName: store.tableName,
|
|
7365
|
-
Key: itemKey(DYNAMODB_CHANNEL_PARTITION, current.sk),
|
|
7366
|
-
ConditionExpression: "#version = :version",
|
|
7367
|
-
ExpressionAttributeNames: { "#version": "version" },
|
|
7368
|
-
ExpressionAttributeValues: { ":version": current.version }
|
|
7369
|
-
} }, { Delete: {
|
|
7370
|
-
TableName: store.tableName,
|
|
7371
|
-
Key: {
|
|
7372
|
-
pk: DYNAMODB_CHANNEL_NAME_PARTITION,
|
|
7373
|
-
sk: current.row.name
|
|
7374
|
-
},
|
|
7375
|
-
ConditionExpression: "#channelId = :channelId",
|
|
7376
|
-
ExpressionAttributeNames: { "#channelId": "channel_id" },
|
|
7377
|
-
ExpressionAttributeValues: { ":channelId": current.sk }
|
|
7378
|
-
} }];
|
|
7379
|
-
const updateChannelReferenceCount = (store, current, delta) => ({ Update: {
|
|
7380
|
-
TableName: store.tableName,
|
|
7381
|
-
Key: itemKey(DYNAMODB_CHANNEL_PARTITION, current.sk),
|
|
7382
|
-
ConditionExpression: "#row.#name = :name" + (delta < 0 ? " AND #referenceCount >= :removal" : ""),
|
|
7383
|
-
UpdateExpression: "SET #version = #version + :one ADD #referenceCount :delta",
|
|
7384
|
-
ExpressionAttributeNames: {
|
|
7385
|
-
"#row": "row",
|
|
7386
|
-
"#name": "name",
|
|
7387
|
-
"#version": "version",
|
|
7388
|
-
"#referenceCount": "reference_count"
|
|
7389
|
-
},
|
|
7390
|
-
ExpressionAttributeValues: {
|
|
7391
|
-
":name": current.row.name,
|
|
7392
|
-
":one": 1,
|
|
7393
|
-
":delta": delta,
|
|
7394
|
-
...delta < 0 ? { ":removal": -delta } : {}
|
|
7395
|
-
}
|
|
7396
|
-
} });
|
|
7397
|
-
const loadApiKeyByHash = async (store, hash) => {
|
|
7398
|
-
const { Item } = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
7399
|
-
TableName: store.tableName,
|
|
7400
|
-
Key: {
|
|
7401
|
-
pk: DYNAMODB_API_KEY_HASH_PARTITION,
|
|
7402
|
-
sk: hash
|
|
6452
|
+
return keys.map((key) => found.get(idOf(key)) ?? null);
|
|
7403
6453
|
},
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
const [originalBundleItems, originalPatchItems, originalChannelItems, originalReleaseItems, originalReleaseCatalogItems] = await Promise.all([
|
|
7413
|
-
loadBundleItems(store),
|
|
7414
|
-
loadPatchItems(store),
|
|
7415
|
-
loadChannelItems(store),
|
|
7416
|
-
loadReleaseItems(store),
|
|
7417
|
-
loadReleaseCatalogItems(store)
|
|
7418
|
-
]);
|
|
7419
|
-
const bundles = new Map(originalBundleItems.map(({ sk, row }) => [sk, row]));
|
|
7420
|
-
const patches = new Map(originalPatchItems.map(({ sk, row }) => [sk, row]));
|
|
7421
|
-
const channels = new Map(originalChannelItems.map(({ sk, row }) => [sk, row]));
|
|
7422
|
-
const channelNames = new Map(originalChannelItems.map(({ sk, row }) => [row.name, sk]));
|
|
7423
|
-
const releases = new Map(originalReleaseItems.map(({ sk, row }) => [sk, row]));
|
|
7424
|
-
const releaseCatalogs = new Map(originalReleaseCatalogItems.map(({ sk, row }) => [sk, row]));
|
|
7425
|
-
const originalApiKeys = /* @__PURE__ */ new Map();
|
|
7426
|
-
const apiKeys = /* @__PURE__ */ new Map();
|
|
7427
|
-
const apiKeyHashes = /* @__PURE__ */ new Map();
|
|
7428
|
-
const insights = /* @__PURE__ */ new Map();
|
|
7429
|
-
const rememberApiKey = (value) => {
|
|
7430
|
-
if (value === null || apiKeys.has(value.row.id)) return;
|
|
7431
|
-
originalApiKeys.set(value.row.id, value);
|
|
7432
|
-
apiKeys.set(value.row.id, value);
|
|
7433
|
-
apiKeyHashes.set(value.row.hash, value.row.id);
|
|
7434
|
-
};
|
|
7435
|
-
const requireReleaseReferences = (row) => {
|
|
7436
|
-
if (!channels.has(row.channel_id)) throw new DynamoDBCommitStateError(`Release "${row.id}" references an invalid channel`);
|
|
7437
|
-
if (row.bundle_id !== null) {
|
|
7438
|
-
const bundle = bundles.get(row.bundle_id);
|
|
7439
|
-
if (bundle === void 0 || bundle.platform !== row.platform) throw new DynamoDBCommitStateError(`Release "${row.id}" references an invalid bundle`);
|
|
7440
|
-
}
|
|
7441
|
-
};
|
|
7442
|
-
for (const expectation of input.expectations ?? []) {
|
|
7443
|
-
const actualVersion = expectation.model === "releases" ? releases.get(expectation.id)?.revision ?? null : releaseCatalogs.get(expectation.scopeKey)?.generation ?? null;
|
|
7444
|
-
const expectedVersion = expectation.model === "releases" ? expectation.revision : expectation.generation;
|
|
7445
|
-
if (actualVersion !== expectedVersion) return {
|
|
7446
|
-
committed: false,
|
|
7447
|
-
conflict: {
|
|
7448
|
-
actualVersion,
|
|
7449
|
-
changeIndex: -1,
|
|
7450
|
-
expectedVersion,
|
|
7451
|
-
key: expectation.model === "releases" ? expectation.id : expectation.scopeKey,
|
|
7452
|
-
model: expectation.model,
|
|
7453
|
-
reason: "version_conflict"
|
|
7454
|
-
}
|
|
7455
|
-
};
|
|
7456
|
-
}
|
|
7457
|
-
for (const [changeIndex, change] of input.changes.entries()) switch (change.model) {
|
|
7458
|
-
case "channels":
|
|
7459
|
-
if (change.operation === "insert") {
|
|
7460
|
-
if (channelNames.get(change.row.name) !== void 0) break;
|
|
7461
|
-
if (channels.get(change.row.id) !== void 0) throw new DynamoDBCommitStateError(`Channel id "${change.row.id}" already exists`);
|
|
7462
|
-
channels.set(change.row.id, change.row);
|
|
7463
|
-
channelNames.set(change.row.name, change.row.id);
|
|
7464
|
-
} else {
|
|
7465
|
-
const current = channels.get(change.where.id);
|
|
7466
|
-
if (current === void 0) break;
|
|
7467
|
-
if ([...releases.values()].some(({ channel_id }) => channel_id === change.where.id)) return {
|
|
7468
|
-
committed: false,
|
|
7469
|
-
conflict: {
|
|
7470
|
-
changeIndex,
|
|
7471
|
-
reason: "referenced"
|
|
7472
|
-
}
|
|
7473
|
-
};
|
|
7474
|
-
channels.delete(change.where.id);
|
|
7475
|
-
channelNames.delete(current.name);
|
|
7476
|
-
}
|
|
7477
|
-
break;
|
|
7478
|
-
case "bundles":
|
|
7479
|
-
if (change.operation === "insert") {
|
|
7480
|
-
if (bundles.has(change.row.id)) throw new DynamoDBCommitStateError(`Bundle "${change.row.id}" already exists`);
|
|
7481
|
-
bundles.set(change.row.id, change.row);
|
|
7482
|
-
} else {
|
|
7483
|
-
const current = bundles.get(change.where.id);
|
|
7484
|
-
if (current === void 0) {
|
|
7485
|
-
if (change.operation === "update") return {
|
|
7486
|
-
committed: false,
|
|
7487
|
-
conflict: {
|
|
7488
|
-
changeIndex,
|
|
7489
|
-
reason: "not_found"
|
|
7490
|
-
}
|
|
7491
|
-
};
|
|
7492
|
-
break;
|
|
7493
|
-
}
|
|
7494
|
-
if (change.operation === "update") {
|
|
7495
|
-
const row = {
|
|
7496
|
-
...current,
|
|
7497
|
-
...change.update
|
|
7498
|
-
};
|
|
7499
|
-
bundles.set(row.id, row);
|
|
7500
|
-
} else {
|
|
7501
|
-
if ([...releases.values()].some(({ bundle_id }) => bundle_id === change.where.id)) return {
|
|
7502
|
-
committed: false,
|
|
7503
|
-
conflict: {
|
|
7504
|
-
changeIndex,
|
|
7505
|
-
reason: "referenced"
|
|
7506
|
-
}
|
|
7507
|
-
};
|
|
7508
|
-
bundles.delete(change.where.id);
|
|
7509
|
-
for (const [id, patch] of patches) if (patch.bundle_id === change.where.id || patch.base_bundle_id === change.where.id) patches.delete(id);
|
|
7510
|
-
}
|
|
7511
|
-
}
|
|
7512
|
-
break;
|
|
7513
|
-
case "releases":
|
|
7514
|
-
if (change.operation === "insert") {
|
|
7515
|
-
requireReleaseReferences(change.row);
|
|
7516
|
-
if (releases.has(change.row.id)) throw new DynamoDBCommitStateError(`Release "${change.row.id}" already exists`);
|
|
7517
|
-
releases.set(change.row.id, change.row);
|
|
7518
|
-
} else {
|
|
7519
|
-
const current = releases.get(change.where.id);
|
|
7520
|
-
if (current === void 0) {
|
|
7521
|
-
if (change.operation === "update") return {
|
|
7522
|
-
committed: false,
|
|
7523
|
-
conflict: {
|
|
7524
|
-
changeIndex,
|
|
7525
|
-
reason: "not_found"
|
|
7526
|
-
}
|
|
7527
|
-
};
|
|
7528
|
-
break;
|
|
7529
|
-
}
|
|
7530
|
-
if (change.operation === "update") {
|
|
7531
|
-
const row = {
|
|
7532
|
-
...current,
|
|
7533
|
-
...change.update
|
|
7534
|
-
};
|
|
7535
|
-
requireReleaseReferences(row);
|
|
7536
|
-
releases.set(row.id, row);
|
|
7537
|
-
} else releases.delete(change.where.id);
|
|
7538
|
-
}
|
|
7539
|
-
break;
|
|
7540
|
-
case "releaseCatalogs":
|
|
7541
|
-
if (!channels.has(change.row.channel_id)) throw new DynamoDBCommitStateError(`Release catalog "${change.row.scope_key}" references an invalid channel`);
|
|
7542
|
-
releaseCatalogs.set(change.row.scope_key, change.row);
|
|
7543
|
-
break;
|
|
7544
|
-
case "bundlePatches":
|
|
7545
|
-
if (change.operation === "insert") {
|
|
7546
|
-
if (!bundles.has(change.row.bundle_id) || !bundles.has(change.row.base_bundle_id)) throw new DynamoDBCommitStateError(`Patch "${change.row.id}" references a missing bundle`);
|
|
7547
|
-
if (patches.has(change.row.id)) throw new DynamoDBDuplicatePatchError(change.row.id);
|
|
7548
|
-
patches.set(change.row.id, change.row);
|
|
7549
|
-
} else for (const [id, patch] of patches) if (patch.bundle_id === change.where.bundleId) patches.delete(id);
|
|
7550
|
-
break;
|
|
7551
|
-
case "insights": {
|
|
7552
|
-
const key = insightsSortKey(change.row);
|
|
7553
|
-
if (insights.has(key)) throw new DynamoDBCommitStateError(`Insights event "${change.row.id}" is duplicated`);
|
|
7554
|
-
insights.set(key, change.row);
|
|
7555
|
-
break;
|
|
7556
|
-
}
|
|
7557
|
-
case "apiKeys":
|
|
7558
|
-
if (change.operation === "insert") {
|
|
7559
|
-
rememberApiKey(await loadApiKeyByHash(store, change.row.hash));
|
|
7560
|
-
if (apiKeyHashes.has(change.row.hash)) break;
|
|
7561
|
-
rememberApiKey(await loadApiKey(store, change.row.id));
|
|
7562
|
-
if (apiKeys.has(change.row.id)) throw new DynamoDBCommitStateError(`API key id "${change.row.id}" already exists`);
|
|
7563
|
-
const inserted = {
|
|
7564
|
-
row: change.row,
|
|
7565
|
-
version: 1
|
|
7566
|
-
};
|
|
7567
|
-
apiKeys.set(change.row.id, inserted);
|
|
7568
|
-
apiKeyHashes.set(change.row.hash, change.row.id);
|
|
7569
|
-
} else {
|
|
7570
|
-
rememberApiKey(await loadApiKey(store, change.where.id));
|
|
7571
|
-
const current = apiKeys.get(change.where.id);
|
|
7572
|
-
if (current === void 0) return {
|
|
7573
|
-
committed: false,
|
|
7574
|
-
conflict: {
|
|
7575
|
-
changeIndex,
|
|
7576
|
-
reason: "not_found"
|
|
7577
|
-
}
|
|
7578
|
-
};
|
|
7579
|
-
apiKeys.set(change.where.id, {
|
|
7580
|
-
row: {
|
|
7581
|
-
...current.row,
|
|
7582
|
-
revoked_at_ms: change.update.revokedAtMs
|
|
7583
|
-
},
|
|
7584
|
-
version: current.version
|
|
7585
|
-
});
|
|
7586
|
-
}
|
|
7587
|
-
break;
|
|
7588
|
-
}
|
|
7589
|
-
const relationCounts = /* @__PURE__ */ new Map();
|
|
7590
|
-
const ownedPatchCounts = /* @__PURE__ */ new Map();
|
|
7591
|
-
for (const patch of patches.values()) {
|
|
7592
|
-
for (const id of new Set([patch.bundle_id, patch.base_bundle_id])) relationCounts.set(id, (relationCounts.get(id) ?? 0) + 1);
|
|
7593
|
-
ownedPatchCounts.set(patch.bundle_id, (ownedPatchCounts.get(patch.bundle_id) ?? 0) + 1);
|
|
7594
|
-
}
|
|
7595
|
-
const channelReferenceCounts = /* @__PURE__ */ new Map();
|
|
7596
|
-
for (const release of releases.values()) channelReferenceCounts.set(release.channel_id, (channelReferenceCounts.get(release.channel_id) ?? 0) + 1);
|
|
7597
|
-
const actions = [];
|
|
7598
|
-
const originalChannels = new Map(originalChannelItems.map((item) => [item.sk, item]));
|
|
7599
|
-
for (const original of originalChannelItems) if (!channels.has(original.sk)) actions.push(...deleteChannelActions(store, original));
|
|
7600
|
-
for (const [id, row] of channels) {
|
|
7601
|
-
const original = originalChannels.get(id);
|
|
7602
|
-
const referenceCount = channelReferenceCounts.get(id) ?? 0;
|
|
7603
|
-
if (original === void 0) {
|
|
7604
|
-
actions.push(putChannel(store, row, original, referenceCount));
|
|
7605
|
-
actions.push({ Put: {
|
|
7606
|
-
TableName: store.tableName,
|
|
7607
|
-
Item: {
|
|
7608
|
-
pk: DYNAMODB_CHANNEL_NAME_PARTITION,
|
|
7609
|
-
sk: row.name,
|
|
7610
|
-
channel_id: id
|
|
7611
|
-
},
|
|
7612
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7613
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7614
|
-
} });
|
|
7615
|
-
} else {
|
|
7616
|
-
if (!rowsEqual(original.row, row)) throw new DynamoDBCommitStateError(`Channel "${id}" cannot be updated`);
|
|
7617
|
-
const delta = referenceCount - original.reference_count;
|
|
7618
|
-
if (delta !== 0) actions.push(updateChannelReferenceCount(store, original, delta));
|
|
7619
|
-
}
|
|
7620
|
-
}
|
|
7621
|
-
const originalBundles = new Map(originalBundleItems.map((item) => [item.sk, item]));
|
|
7622
|
-
for (const original of originalBundleItems) if (!bundles.has(original.sk)) actions.push(deleteAction(store, original));
|
|
7623
|
-
for (const [id, row] of bundles) {
|
|
7624
|
-
const original = originalBundles.get(id);
|
|
7625
|
-
const relationCount = relationCounts.get(id) ?? 0;
|
|
7626
|
-
const ownedPatchCount = ownedPatchCounts.get(id) ?? 0;
|
|
7627
|
-
if (original === void 0) actions.push(putNewBundle(store, row, relationCount, ownedPatchCount));
|
|
7628
|
-
else if (!rowsEqual(original.row, row)) actions.push(putUpdatedBundle(store, original, row, relationCount, ownedPatchCount));
|
|
7629
|
-
else {
|
|
7630
|
-
const relationDelta = relationCount - original.relation_count;
|
|
7631
|
-
const ownedPatchDelta = ownedPatchCount - original.owned_patch_count;
|
|
7632
|
-
if (relationDelta !== 0 || ownedPatchDelta !== 0) actions.push(updateBundleRelation(store, id, relationDelta, ownedPatchDelta));
|
|
7633
|
-
}
|
|
7634
|
-
}
|
|
7635
|
-
const originalPatches = new Map(originalPatchItems.map((item) => [item.sk, item]));
|
|
7636
|
-
for (const original of originalPatchItems) if (!patches.has(original.sk)) actions.push(deletePatch(store, original));
|
|
7637
|
-
for (const [id, row] of patches) {
|
|
7638
|
-
const original = originalPatches.get(id);
|
|
7639
|
-
if (original === void 0 || !rowsEqual(original.row, row)) actions.push(putPatch(store, row, original));
|
|
7640
|
-
}
|
|
7641
|
-
const originalReleases = new Map(originalReleaseItems.map((item) => [item.sk, item]));
|
|
7642
|
-
for (const original of originalReleaseItems) {
|
|
7643
|
-
if (releases.get(original.sk) !== void 0) continue;
|
|
7644
|
-
actions.push(deleteAction(store, original));
|
|
7645
|
-
actions.push({ Delete: {
|
|
7646
|
-
TableName: store.tableName,
|
|
7647
|
-
Key: {
|
|
7648
|
-
pk: DYNAMODB_RELEASE_ID_PARTITION,
|
|
7649
|
-
sk: original.sk
|
|
7650
|
-
},
|
|
7651
|
-
ConditionExpression: "#scopeKey = :scopeKey",
|
|
7652
|
-
ExpressionAttributeNames: { "#scopeKey": "scope_key" },
|
|
7653
|
-
ExpressionAttributeValues: { ":scopeKey": original.row.scope_key }
|
|
7654
|
-
} });
|
|
7655
|
-
}
|
|
7656
|
-
for (const [id, row] of releases) {
|
|
7657
|
-
const original = originalReleases.get(id);
|
|
7658
|
-
if (original === void 0) {
|
|
7659
|
-
actions.push({ Put: {
|
|
7660
|
-
TableName: store.tableName,
|
|
7661
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBReleaseItem(row)),
|
|
7662
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7663
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7664
|
-
} });
|
|
7665
|
-
actions.push({ Put: {
|
|
7666
|
-
TableName: store.tableName,
|
|
7667
|
-
Item: releaseLocatorItem(row),
|
|
7668
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7669
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7670
|
-
} });
|
|
7671
|
-
} else if (!rowsEqual(original.row, row)) {
|
|
7672
|
-
const moved = original.row.scope_key !== row.scope_key;
|
|
7673
|
-
if (moved) actions.push(deleteAction(store, original));
|
|
7674
|
-
actions.push({ Put: {
|
|
7675
|
-
TableName: store.tableName,
|
|
7676
|
-
Item: boundedDynamoDBMetadataItem(toDynamoDBReleaseItem(row, original.version + 1)),
|
|
7677
|
-
ConditionExpression: moved ? "attribute_not_exists(#pk)" : "#version = :version",
|
|
7678
|
-
ExpressionAttributeNames: moved ? { "#pk": "pk" } : { "#version": "version" },
|
|
7679
|
-
...moved ? {} : { ExpressionAttributeValues: { ":version": original.version } }
|
|
7680
|
-
} });
|
|
7681
|
-
if (moved) actions.push({ Put: {
|
|
7682
|
-
TableName: store.tableName,
|
|
7683
|
-
Item: releaseLocatorItem(row),
|
|
7684
|
-
ConditionExpression: "#scopeKey = :scopeKey",
|
|
7685
|
-
ExpressionAttributeNames: { "#scopeKey": "scope_key" },
|
|
7686
|
-
ExpressionAttributeValues: { ":scopeKey": original.row.scope_key }
|
|
7687
|
-
} });
|
|
7688
|
-
}
|
|
7689
|
-
}
|
|
7690
|
-
const originalReleaseCatalogs = new Map(originalReleaseCatalogItems.map((item) => [item.sk, item]));
|
|
7691
|
-
for (const [scopeKey, row] of releaseCatalogs) {
|
|
7692
|
-
const original = originalReleaseCatalogs.get(scopeKey);
|
|
7693
|
-
if (original === void 0 || !rowsEqual(original.row, row)) actions.push({ Put: {
|
|
7694
|
-
TableName: store.tableName,
|
|
7695
|
-
Item: boundedDynamoDBCatalogItem(toDynamoDBReleaseCatalogItem(row, (original?.version ?? 0) + 1)),
|
|
7696
|
-
ConditionExpression: original ? "#version = :version" : "attribute_not_exists(#pk)",
|
|
7697
|
-
ExpressionAttributeNames: original ? { "#version": "version" } : { "#pk": "pk" },
|
|
7698
|
-
...original ? { ExpressionAttributeValues: { ":version": original.version } } : {}
|
|
7699
|
-
} });
|
|
7700
|
-
}
|
|
7701
|
-
const changedReleaseIds = new Set([...new Set([...originalReleases.keys(), ...releases.keys()])].filter((id) => {
|
|
7702
|
-
const original = originalReleases.get(id)?.row;
|
|
7703
|
-
const next = releases.get(id);
|
|
7704
|
-
return original === void 0 || next === void 0 || !rowsEqual(original, next);
|
|
7705
|
-
}));
|
|
7706
|
-
const changedCatalogScopes = new Set([...releaseCatalogs].flatMap(([scopeKey, row]) => {
|
|
7707
|
-
const original = originalReleaseCatalogs.get(scopeKey)?.row;
|
|
7708
|
-
return original === void 0 || !rowsEqual(original, row) ? [scopeKey] : [];
|
|
7709
|
-
}));
|
|
7710
|
-
for (const expectation of input.expectations ?? []) {
|
|
7711
|
-
if (expectation.model === "releases" && !changedReleaseIds.has(expectation.id)) {
|
|
7712
|
-
const original = originalReleases.get(expectation.id);
|
|
7713
|
-
actions.push({ ConditionCheck: {
|
|
7714
|
-
TableName: store.tableName,
|
|
7715
|
-
Key: original === void 0 ? {
|
|
7716
|
-
pk: DYNAMODB_RELEASE_ID_PARTITION,
|
|
7717
|
-
sk: expectation.id
|
|
7718
|
-
} : {
|
|
7719
|
-
pk: original.pk,
|
|
7720
|
-
sk: original.sk
|
|
7721
|
-
},
|
|
7722
|
-
ConditionExpression: original === void 0 ? "attribute_not_exists(#pk)" : "#version = :version",
|
|
7723
|
-
ExpressionAttributeNames: original === void 0 ? { "#pk": "pk" } : { "#version": "version" },
|
|
7724
|
-
...original === void 0 ? {} : { ExpressionAttributeValues: { ":version": original.version } }
|
|
7725
|
-
} });
|
|
7726
|
-
}
|
|
7727
|
-
if (expectation.model === "releaseCatalogs" && !changedCatalogScopes.has(expectation.scopeKey)) {
|
|
7728
|
-
const original = originalReleaseCatalogs.get(expectation.scopeKey);
|
|
7729
|
-
actions.push({ ConditionCheck: {
|
|
7730
|
-
TableName: store.tableName,
|
|
7731
|
-
Key: itemKey("release_catalogs", expectation.scopeKey),
|
|
7732
|
-
ConditionExpression: original === void 0 ? "attribute_not_exists(#pk)" : "#version = :version",
|
|
7733
|
-
ExpressionAttributeNames: original === void 0 ? { "#pk": "pk" } : { "#version": "version" },
|
|
7734
|
-
...original === void 0 ? {} : { ExpressionAttributeValues: { ":version": original.version } }
|
|
7735
|
-
} });
|
|
7736
|
-
}
|
|
7737
|
-
}
|
|
7738
|
-
const counter = metadataUpdate(store, {
|
|
7739
|
-
bundles: bundles.size - originalBundles.size,
|
|
7740
|
-
bundle_patches: patches.size - originalPatches.size
|
|
7741
|
-
});
|
|
7742
|
-
if (counter !== void 0) actions.push(counter);
|
|
7743
|
-
for (const [key, row] of insights) actions.push({ Put: {
|
|
7744
|
-
TableName: store.tableName,
|
|
7745
|
-
Item: boundedDynamoDBMetadataItem({
|
|
7746
|
-
pk: DYNAMODB_INSIGHTS_PARTITION,
|
|
7747
|
-
sk: key,
|
|
7748
|
-
version: 1,
|
|
7749
|
-
row
|
|
7750
|
-
}),
|
|
7751
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7752
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7753
|
-
} });
|
|
7754
|
-
for (const [id, current] of apiKeys) {
|
|
7755
|
-
const original = originalApiKeys.get(id);
|
|
7756
|
-
if (original !== void 0 && rowsEqual(original.row, current.row)) continue;
|
|
7757
|
-
actions.push({ Put: {
|
|
7758
|
-
TableName: store.tableName,
|
|
7759
|
-
Item: boundedDynamoDBMetadataItem(apiKeyItem(current.row, original === void 0 ? 1 : original.version + 1)),
|
|
7760
|
-
ConditionExpression: original ? "#version = :version" : "attribute_not_exists(#pk)",
|
|
7761
|
-
ExpressionAttributeNames: original ? { "#version": "version" } : { "#pk": "pk" },
|
|
7762
|
-
...original ? { ExpressionAttributeValues: { ":version": original.version } } : {}
|
|
7763
|
-
} });
|
|
7764
|
-
if (original === void 0) actions.push({ Put: {
|
|
7765
|
-
TableName: store.tableName,
|
|
7766
|
-
Item: {
|
|
7767
|
-
pk: DYNAMODB_API_KEY_HASH_PARTITION,
|
|
7768
|
-
sk: current.row.hash,
|
|
7769
|
-
api_key_id: current.row.id
|
|
7770
|
-
},
|
|
7771
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7772
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7773
|
-
} });
|
|
7774
|
-
}
|
|
7775
|
-
if (actions.length > 0) await commitDynamoDBTransaction(store, actions);
|
|
7776
|
-
return { committed: true };
|
|
7777
|
-
};
|
|
7778
|
-
const isDynamoDBTransactionConflict = (error) => typeof error === "object" && error !== null && Reflect.get(error, "name") === "TransactionCanceledException";
|
|
7779
|
-
const createDynamoDBCommit = (store) => async (input) => {
|
|
7780
|
-
try {
|
|
7781
|
-
return await compileAndCommitDynamoDBChanges(store, input);
|
|
7782
|
-
} catch (error) {
|
|
7783
|
-
if (!isDynamoDBTransactionConflict(error)) throw error;
|
|
7784
|
-
return compileAndCommitDynamoDBChanges(store, input);
|
|
7785
|
-
}
|
|
7786
|
-
};
|
|
7787
|
-
const DYNAMODB_INSIGHTS_PARTITION = "bundle_events";
|
|
7788
|
-
const DYNAMODB_API_KEY_PARTITION = "api_keys";
|
|
7789
|
-
const DYNAMODB_API_KEY_HASH_PARTITION = "_hot-updater#api-key-hashes";
|
|
7790
|
-
const hasValidBundleEventShape = (value) => {
|
|
7791
|
-
const type = field(value, "type");
|
|
7792
|
-
const fromBundleId = field(value, "from_bundle_id");
|
|
7793
|
-
const updateStrategy = field(value, "update_strategy");
|
|
7794
|
-
return (type === "UPDATE_APPLIED" || type === "RECOVERED" || type === "RELEASE_ADOPTED") && typeof fromBundleId === "string" && (updateStrategy === "fingerprint" || updateStrategy === "appVersion") || type === "UNCHANGED" && fromBundleId === null && updateStrategy === null;
|
|
7795
|
-
};
|
|
7796
|
-
const isBundleEventRow = (value) => typeof value === "object" && value !== null && typeof field(value, "id") === "string" && typeof field(value, "install_id") === "string" && isNullableString(field(value, "from_release_id")) && isNullableString(field(value, "to_release_id")) && typeof field(value, "to_bundle_id") === "string" && (field(value, "platform") === "ios" || field(value, "platform") === "android") && typeof field(value, "received_at_ms") === "number" && hasValidBundleEventShape(value);
|
|
7797
|
-
const isApiKeyRow = (value) => typeof value === "object" && value !== null && typeof field(value, "id") === "string" && typeof field(value, "hash") === "string" && typeof field(value, "name") === "string" && typeof field(value, "prefix") === "string" && field(value, "role") === "client" && typeof field(value, "created_at_ms") === "number" && (field(value, "revoked_at_ms") === null || typeof field(value, "revoked_at_ms") === "number");
|
|
7798
|
-
const parseOfficialRowItem = (value, partition, isRow) => {
|
|
7799
|
-
if (value.pk !== partition || typeof value.sk !== "string" || typeof value.version !== "number" || !isRow(value.row)) throw new DynamoDBStoredItemError();
|
|
7800
|
-
return {
|
|
7801
|
-
row: value.row,
|
|
7802
|
-
version: value.version
|
|
7803
|
-
};
|
|
7804
|
-
};
|
|
7805
|
-
const timestampSortKey = (timestampMs) => Math.trunc(timestampMs).toString().padStart(16, "0");
|
|
7806
|
-
const insightsSortKey = (row) => `${timestampSortKey(row.received_at_ms)}#${row.id}`;
|
|
7807
|
-
const createDynamoDBInsightsTable = (store) => ({
|
|
7808
|
-
async append(row) {
|
|
7809
|
-
await store.client.send(new _aws_sdk_lib_dynamodb.PutCommand({
|
|
7810
|
-
TableName: store.tableName,
|
|
7811
|
-
Item: boundedDynamoDBMetadataItem({
|
|
7812
|
-
pk: DYNAMODB_INSIGHTS_PARTITION,
|
|
7813
|
-
sk: insightsSortKey(row),
|
|
7814
|
-
version: 1,
|
|
7815
|
-
row
|
|
7816
|
-
}),
|
|
7817
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7818
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7819
|
-
}));
|
|
7820
|
-
},
|
|
7821
|
-
async scan(input) {
|
|
7822
|
-
if (input.limit <= 0) return [];
|
|
7823
|
-
if (input.after !== void 0 && input.after.receivedAtMs >= input.beforeReceivedAtMs) return [];
|
|
7824
|
-
const rows = [];
|
|
7825
|
-
const afterKey = input.after === void 0 ? void 0 : `${timestampSortKey(input.after.receivedAtMs)}#${input.after.id}`;
|
|
7826
|
-
let exclusiveStartKey;
|
|
7827
|
-
do {
|
|
7828
|
-
const page = await store.client.send(new _aws_sdk_lib_dynamodb.QueryCommand({
|
|
7829
|
-
TableName: store.tableName,
|
|
7830
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
7831
|
-
KeyConditionExpression: afterKey === void 0 ? "#pk = :pk AND #sk < :before" : "#pk = :pk AND #sk BETWEEN :after AND :before",
|
|
7832
|
-
ExpressionAttributeNames: {
|
|
7833
|
-
"#pk": "pk",
|
|
7834
|
-
"#sk": "sk"
|
|
7835
|
-
},
|
|
6454
|
+
async query({ pk, gte, lt, lte, order, limit, after }) {
|
|
6455
|
+
let condition = "pk = :pk";
|
|
6456
|
+
if (gte !== void 0 && lt !== void 0) condition += " AND sk BETWEEN :gte AND :lte";
|
|
6457
|
+
else if (gte !== void 0) condition += " AND sk >= :gte";
|
|
6458
|
+
else if (lt !== void 0) condition += " AND sk < :lt";
|
|
6459
|
+
const result = await documents.send(new _aws_sdk_lib_dynamodb.QueryCommand({
|
|
6460
|
+
TableName: tableName,
|
|
6461
|
+
KeyConditionExpression: condition,
|
|
7836
6462
|
ExpressionAttributeValues: {
|
|
7837
|
-
":
|
|
7838
|
-
":
|
|
7839
|
-
...
|
|
6463
|
+
":pk": pk,
|
|
6464
|
+
":gte": gte,
|
|
6465
|
+
...gte !== void 0 && lt !== void 0 ? { ":lte": lte ?? lt } : { ":lt": lt }
|
|
7840
6466
|
},
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
}
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
})
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
sk: row.id,
|
|
7857
|
-
version,
|
|
7858
|
-
row
|
|
7859
|
-
});
|
|
7860
|
-
const loadApiKey = async (store, id) => {
|
|
7861
|
-
const result = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
7862
|
-
TableName: store.tableName,
|
|
7863
|
-
Key: {
|
|
7864
|
-
pk: DYNAMODB_API_KEY_PARTITION,
|
|
7865
|
-
sk: id
|
|
6467
|
+
ScanIndexForward: order === "asc",
|
|
6468
|
+
ConsistentRead: true,
|
|
6469
|
+
Limit: Math.min(limit, nativePageSize ?? limit),
|
|
6470
|
+
ExclusiveStartKey: after === void 0 ? void 0 : {
|
|
6471
|
+
pk,
|
|
6472
|
+
sk: after
|
|
6473
|
+
}
|
|
6474
|
+
})).catch(missingTable);
|
|
6475
|
+
return {
|
|
6476
|
+
items: (result.Items ?? []).map((item) => ({
|
|
6477
|
+
sk: item.sk,
|
|
6478
|
+
value: toRow(item)
|
|
6479
|
+
})),
|
|
6480
|
+
more: result.LastEvaluatedKey !== void 0
|
|
6481
|
+
};
|
|
7866
6482
|
},
|
|
7867
|
-
|
|
7868
|
-
}));
|
|
7869
|
-
return result.Item === void 0 ? null : parseOfficialRowItem(result.Item, DYNAMODB_API_KEY_PARTITION, isApiKeyRow);
|
|
7870
|
-
};
|
|
7871
|
-
const createDynamoDBApiKeyTable = (store) => {
|
|
7872
|
-
const findByHash = async (hash) => {
|
|
7873
|
-
const lookup = await store.client.send(new _aws_sdk_lib_dynamodb.GetCommand({
|
|
7874
|
-
TableName: store.tableName,
|
|
7875
|
-
Key: {
|
|
7876
|
-
pk: DYNAMODB_API_KEY_HASH_PARTITION,
|
|
7877
|
-
sk: hash
|
|
7878
|
-
},
|
|
7879
|
-
ConsistentRead: true
|
|
7880
|
-
}));
|
|
7881
|
-
const id = lookup.Item?.api_key_id;
|
|
7882
|
-
if (lookup.Item === void 0) return null;
|
|
7883
|
-
if (typeof id !== "string") throw new DynamoDBStoredItemError();
|
|
7884
|
-
return (await loadApiKey(store, id))?.row ?? null;
|
|
7885
|
-
};
|
|
7886
|
-
return {
|
|
7887
|
-
async create(row) {
|
|
7888
|
-
if (await findByHash(row.hash) !== null) return "existing";
|
|
6483
|
+
async write(ops) {
|
|
7889
6484
|
try {
|
|
7890
|
-
await
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
} }, { Put: {
|
|
7896
|
-
TableName: store.tableName,
|
|
7897
|
-
Item: {
|
|
7898
|
-
pk: DYNAMODB_API_KEY_HASH_PARTITION,
|
|
7899
|
-
sk: row.hash,
|
|
7900
|
-
api_key_id: row.id
|
|
7901
|
-
},
|
|
7902
|
-
ConditionExpression: "attribute_not_exists(#pk)",
|
|
7903
|
-
ExpressionAttributeNames: { "#pk": "pk" }
|
|
7904
|
-
} }]);
|
|
7905
|
-
return "created";
|
|
6485
|
+
await documents.send(new _aws_sdk_lib_dynamodb.TransactWriteCommand({
|
|
6486
|
+
TransactItems: ops.map((op) => toTransactItem(tableName, op)),
|
|
6487
|
+
ClientRequestToken: crypto.randomUUID()
|
|
6488
|
+
}));
|
|
6489
|
+
return { ok: true };
|
|
7906
6490
|
} catch (error) {
|
|
7907
|
-
|
|
7908
|
-
|
|
6491
|
+
const { CancellationReasons = [] } = error;
|
|
6492
|
+
const codes = CancellationReasons.map(({ Code }) => Code);
|
|
6493
|
+
const failed = codes.indexOf("ConditionalCheckFailed");
|
|
6494
|
+
if (failed !== -1) return {
|
|
6495
|
+
ok: false,
|
|
6496
|
+
failedOp: failed
|
|
6497
|
+
};
|
|
6498
|
+
return [nameOf(error), ...codes].some((code) => TRANSIENT.has(code)) ? {
|
|
6499
|
+
ok: false,
|
|
6500
|
+
retry: true
|
|
6501
|
+
} : missingTable(error);
|
|
7909
6502
|
}
|
|
7910
6503
|
},
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
TableName: store.tableName,
|
|
7938
|
-
Item: boundedDynamoDBMetadataItem(apiKeyItem(row, current.version + 1)),
|
|
7939
|
-
ConditionExpression: "#version = :currentVersion",
|
|
7940
|
-
ExpressionAttributeNames: { "#version": "version" },
|
|
7941
|
-
ExpressionAttributeValues: { ":currentVersion": current.version }
|
|
6504
|
+
migrations: {
|
|
6505
|
+
/** Creates the table for local and test runs; `hot-updater init` creates it in AWS. */
|
|
6506
|
+
async apply() {
|
|
6507
|
+
try {
|
|
6508
|
+
await client.send(new _aws_sdk_client_dynamodb.DescribeTableCommand({ TableName: tableName }));
|
|
6509
|
+
return;
|
|
6510
|
+
} catch (error) {
|
|
6511
|
+
if (nameOf(error) !== "ResourceNotFoundException") throw error;
|
|
6512
|
+
}
|
|
6513
|
+
await client.send(new _aws_sdk_client_dynamodb.CreateTableCommand({
|
|
6514
|
+
TableName: tableName,
|
|
6515
|
+
AttributeDefinitions: [{
|
|
6516
|
+
AttributeName: "pk",
|
|
6517
|
+
AttributeType: "S"
|
|
6518
|
+
}, {
|
|
6519
|
+
AttributeName: "sk",
|
|
6520
|
+
AttributeType: "S"
|
|
6521
|
+
}],
|
|
6522
|
+
KeySchema: [{
|
|
6523
|
+
AttributeName: "pk",
|
|
6524
|
+
KeyType: "HASH"
|
|
6525
|
+
}, {
|
|
6526
|
+
AttributeName: "sk",
|
|
6527
|
+
KeyType: "RANGE"
|
|
6528
|
+
}],
|
|
6529
|
+
BillingMode: "PAY_PER_REQUEST"
|
|
7942
6530
|
}));
|
|
7943
|
-
|
|
7944
|
-
|
|
6531
|
+
await (0, _aws_sdk_client_dynamodb.waitUntilTableExists)({
|
|
6532
|
+
client,
|
|
6533
|
+
maxWaitTime: 120,
|
|
6534
|
+
minDelay: 1,
|
|
6535
|
+
maxDelay: 1
|
|
6536
|
+
}, { TableName: tableName });
|
|
6537
|
+
} }
|
|
7945
6538
|
};
|
|
7946
6539
|
};
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
const
|
|
7952
|
-
|
|
7953
|
-
region: clientConfig.region
|
|
7954
|
-
}) : null;
|
|
7955
|
-
const store = {
|
|
6540
|
+
//#endregion
|
|
6541
|
+
//#region src/dynamoDB.ts
|
|
6542
|
+
/** The DynamoDB client ignores the CloudFront settings in its config. */
|
|
6543
|
+
const adapterOf = ({ tableName, ...clientConfig }) => {
|
|
6544
|
+
const client = new _aws_sdk_client_dynamodb.DynamoDBClient(clientConfig);
|
|
6545
|
+
return {
|
|
7956
6546
|
client,
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
if (!cloudFront || !cloudfrontDistributionId) return;
|
|
7962
|
-
try {
|
|
7963
|
-
await invalidateCloudFront(cloudFront, cloudfrontDistributionId, [`${apiBasePath.replace(/\/+$/, "")}/*`], { shouldWait: shouldWaitForInvalidation });
|
|
7964
|
-
} catch (error) {
|
|
7965
|
-
console.warn("[hot-updater/aws] CloudFront invalidation failed; continuing without cache invalidation.", {
|
|
7966
|
-
distributionId: cloudfrontDistributionId,
|
|
7967
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
7968
|
-
});
|
|
7969
|
-
}
|
|
6547
|
+
adapter: (0, _hot_updater_server_database.createKvAdapter)({ store: createDynamoDBStore({
|
|
6548
|
+
client,
|
|
6549
|
+
tableName
|
|
6550
|
+
}) })
|
|
7970
6551
|
};
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
6552
|
+
};
|
|
6553
|
+
/**
|
|
6554
|
+
* Creates the table when it is missing and writes the schema settings the
|
|
6555
|
+
* database checks before its first read. `hot-updater init` runs it in AWS.
|
|
6556
|
+
*/
|
|
6557
|
+
const migrateDynamoDB = async (config) => {
|
|
6558
|
+
const { client, adapter } = adapterOf(config);
|
|
6559
|
+
try {
|
|
6560
|
+
await (0, _hot_updater_server_database.migrateBuiltInSchema)(adapter, "dynamoDB");
|
|
6561
|
+
} finally {
|
|
6562
|
+
client.destroy();
|
|
6563
|
+
}
|
|
6564
|
+
};
|
|
6565
|
+
/**
|
|
6566
|
+
* Hot Updater's database on one DynamoDB table, through the storage engine.
|
|
6567
|
+
* With a distribution, it gives core the CloudFront invalidation core runs
|
|
6568
|
+
* after a write that changes what the update-check routes answer.
|
|
6569
|
+
*/
|
|
6570
|
+
const dynamoDB = (config) => {
|
|
6571
|
+
const { client, adapter } = adapterOf(config);
|
|
6572
|
+
const cloudFront = createUpdateRouteInvalidation(config);
|
|
6573
|
+
return {
|
|
6574
|
+
...(0, _hot_updater_server_database.createEngineDatabase)({
|
|
6575
|
+
name: "dynamoDB",
|
|
6576
|
+
adapter,
|
|
6577
|
+
onCachedRoutesChange: cloudFront?.invalidate
|
|
6578
|
+
}),
|
|
6579
|
+
async dispose() {
|
|
6580
|
+
await adapter.dispose?.();
|
|
7975
6581
|
client.destroy();
|
|
7976
6582
|
cloudFront?.destroy();
|
|
7977
6583
|
}
|
|
7978
|
-
}
|
|
7979
|
-
return (0, _hot_updater_plugin_core.createDatabasePlugin)({
|
|
7980
|
-
name: "dynamoDB",
|
|
7981
|
-
models: {
|
|
7982
|
-
...adapter.models,
|
|
7983
|
-
bundlePatches: createDynamoDBBundlePatchTable(store, DYNAMODB_UPDATE_INDEX_NAME),
|
|
7984
|
-
insights: createDynamoDBInsightsTable(store),
|
|
7985
|
-
apiKeys: createDynamoDBApiKeyTable(store)
|
|
7986
|
-
},
|
|
7987
|
-
async commit(input) {
|
|
7988
|
-
const result = await adapter.commit(input);
|
|
7989
|
-
if (result.committed && input.changes.some(({ model }) => model === "bundles" || model === "bundlePatches")) await invalidateUpdateRoutes();
|
|
7990
|
-
return result;
|
|
7991
|
-
},
|
|
7992
|
-
dispose: adapter.dispose
|
|
7993
|
-
});
|
|
6584
|
+
};
|
|
7994
6585
|
};
|
|
7995
6586
|
//#endregion
|
|
6587
|
+
//#region src/plugins.ts
|
|
6588
|
+
/**
|
|
6589
|
+
* The plugins the managed AWS server runs: Insights, and API keys on every
|
|
6590
|
+
* client route. Its Lambda@Edge function imports this list.
|
|
6591
|
+
*/
|
|
6592
|
+
const plugins = [(0, _hot_updater_server_plugins_insights.insights)(), (0, _hot_updater_server_plugins_api_keys.apiKeys)()];
|
|
6593
|
+
//#endregion
|
|
7996
6594
|
//#region iac/init/index.ts
|
|
7997
6595
|
const AWS_AUTH_MODES = [
|
|
7998
6596
|
"local-session",
|
|
@@ -8881,7 +7479,7 @@ var CloudFrontManager = class {
|
|
|
8881
7479
|
return policyId;
|
|
8882
7480
|
}
|
|
8883
7481
|
async getOrCreateKeyGroup(publicKey) {
|
|
8884
|
-
const publicKeyHash = crypto.default.createHash("sha256").update(publicKey).digest("hex").slice(0, 16);
|
|
7482
|
+
const publicKeyHash = crypto$1.default.createHash("sha256").update(publicKey).digest("hex").slice(0, 16);
|
|
8885
7483
|
const cloudfrontClient = new _aws_sdk_client_cloudfront.CloudFront({
|
|
8886
7484
|
region: this.region,
|
|
8887
7485
|
credentials: this.credentials
|
|
@@ -9106,13 +7704,16 @@ var CloudFrontManager = class {
|
|
|
9106
7704
|
//#region iac/dynamodb.ts
|
|
9107
7705
|
const DYNAMODB_DESCRIBE_TABLE_ACTION = "dynamodb:DescribeTable";
|
|
9108
7706
|
var DynamoDBTableSchemaError = class extends Error {
|
|
7707
|
+
tableName;
|
|
9109
7708
|
name = "DynamoDBTableSchemaError";
|
|
9110
7709
|
constructor(tableName) {
|
|
9111
|
-
super(`DynamoDB table "${tableName}" does not match the Hot Updater key and index
|
|
7710
|
+
super(`DynamoDB table "${tableName}" does not match the Hot Updater schema: a pk and sk key and no secondary index. A table from before 1.0 keeps its update index; delete it and rerun init to create it again.`);
|
|
9112
7711
|
this.tableName = tableName;
|
|
9113
7712
|
}
|
|
9114
7713
|
};
|
|
9115
7714
|
var DynamoDBPermissionError = class extends _hot_updater_cli_tools.InitError {
|
|
7715
|
+
tableName;
|
|
7716
|
+
region;
|
|
9116
7717
|
name = "DynamoDBPermissionError";
|
|
9117
7718
|
requiredAction = DYNAMODB_DESCRIBE_TABLE_ACTION;
|
|
9118
7719
|
constructor(tableName, region, cause) {
|
|
@@ -9135,30 +7736,47 @@ const primaryKeySchema = [{
|
|
|
9135
7736
|
AttributeName: "sk",
|
|
9136
7737
|
KeyType: "RANGE"
|
|
9137
7738
|
}];
|
|
9138
|
-
const
|
|
9139
|
-
AttributeName: "gsi1pk",
|
|
9140
|
-
KeyType: "HASH"
|
|
9141
|
-
}, {
|
|
9142
|
-
AttributeName: "gsi1sk",
|
|
9143
|
-
KeyType: "RANGE"
|
|
9144
|
-
}];
|
|
9145
|
-
const keyAttributes = [
|
|
9146
|
-
"pk",
|
|
9147
|
-
"sk",
|
|
9148
|
-
"gsi1pk",
|
|
9149
|
-
"gsi1sk"
|
|
9150
|
-
];
|
|
7739
|
+
const keyAttributes = ["pk", "sk"];
|
|
9151
7740
|
const onDemandThroughput = {
|
|
9152
7741
|
MaxReadRequestUnits: 4e3,
|
|
9153
7742
|
MaxWriteRequestUnits: 100
|
|
9154
7743
|
};
|
|
9155
7744
|
const hasKeySchema = (actual, expected) => actual?.length === expected.length && expected.every(({ AttributeName, KeyType }) => actual.some((key) => key.AttributeName === AttributeName && key.KeyType === KeyType));
|
|
9156
7745
|
const hasExpectedSchema = (table) => {
|
|
9157
|
-
|
|
9158
|
-
return keyAttributes.every((attributeName) => table?.AttributeDefinitions?.some(({ AttributeName, AttributeType }) => AttributeName === attributeName && AttributeType === "S")) && table?.BillingModeSummary?.BillingMode === "PAY_PER_REQUEST" && table.OnDemandThroughput?.MaxReadRequestUnits === onDemandThroughput.MaxReadRequestUnits && table.OnDemandThroughput?.MaxWriteRequestUnits === onDemandThroughput.MaxWriteRequestUnits && hasKeySchema(table?.KeySchema, primaryKeySchema) && hasKeySchema(updateIndex?.KeySchema, updateIndexKeySchema) && updateIndex?.OnDemandThroughput?.MaxReadRequestUnits === onDemandThroughput.MaxReadRequestUnits && updateIndex?.OnDemandThroughput?.MaxWriteRequestUnits === onDemandThroughput.MaxWriteRequestUnits && updateIndex?.Projection?.ProjectionType === "ALL";
|
|
7746
|
+
return keyAttributes.every((attributeName) => table?.AttributeDefinitions?.some(({ AttributeName, AttributeType }) => AttributeName === attributeName && AttributeType === "S")) && table?.BillingModeSummary?.BillingMode === "PAY_PER_REQUEST" && table.OnDemandThroughput?.MaxReadRequestUnits === onDemandThroughput.MaxReadRequestUnits && table.OnDemandThroughput?.MaxWriteRequestUnits === onDemandThroughput.MaxWriteRequestUnits && hasKeySchema(table?.KeySchema, primaryKeySchema) && (table?.GlobalSecondaryIndexes ?? []).length === 0;
|
|
9159
7747
|
};
|
|
9160
7748
|
const isResourceNotFound = (error) => typeof error === "object" && error !== null && Reflect.get(error, "name") === "ResourceNotFoundException";
|
|
7749
|
+
const buildDynamoDBCreateTableInput = (tableName) => ({
|
|
7750
|
+
AttributeDefinitions: [{
|
|
7751
|
+
AttributeName: "pk",
|
|
7752
|
+
AttributeType: "S"
|
|
7753
|
+
}, {
|
|
7754
|
+
AttributeName: "sk",
|
|
7755
|
+
AttributeType: "S"
|
|
7756
|
+
}],
|
|
7757
|
+
BillingMode: "PAY_PER_REQUEST",
|
|
7758
|
+
DeletionProtectionEnabled: true,
|
|
7759
|
+
KeySchema: [...primaryKeySchema],
|
|
7760
|
+
OnDemandThroughput: onDemandThroughput,
|
|
7761
|
+
TableName: tableName
|
|
7762
|
+
});
|
|
7763
|
+
/**
|
|
7764
|
+
* The schema settings items the plugin checks before its first read, as one
|
|
7765
|
+
* BatchWriteItem request: what `migrateDynamoDB` writes to a new table.
|
|
7766
|
+
*/
|
|
7767
|
+
const buildDynamoDBSchemaSettingsInput = (tableName) => ({ RequestItems: { [tableName]: Object.entries(_hot_updater_server_database.builtInSettings).map(([key, value]) => ({ PutRequest: { Item: {
|
|
7768
|
+
pk: { S: _hot_updater_server_database.SETTINGS_TABLE.name },
|
|
7769
|
+
sk: { S: (0, _hot_updater_server_database.encodeKvKey)([key]) },
|
|
7770
|
+
key: { S: key },
|
|
7771
|
+
value: { S: value },
|
|
7772
|
+
_v: { N: "0" }
|
|
7773
|
+
} } })) } });
|
|
7774
|
+
const buildDynamoDBBackupInput = (tableName) => ({
|
|
7775
|
+
PointInTimeRecoverySpecification: { PointInTimeRecoveryEnabled: true },
|
|
7776
|
+
TableName: tableName
|
|
7777
|
+
});
|
|
9161
7778
|
var DynamoDBManager = class {
|
|
7779
|
+
region;
|
|
9162
7780
|
client;
|
|
9163
7781
|
constructor(region, credentials) {
|
|
9164
7782
|
this.region = region;
|
|
@@ -9177,37 +7795,7 @@ var DynamoDBManager = class {
|
|
|
9177
7795
|
if (error instanceof Error && error.name === "AccessDeniedException" && error.message.includes(DYNAMODB_DESCRIBE_TABLE_ACTION)) throw new DynamoDBPermissionError(tableName, this.region, error);
|
|
9178
7796
|
if (!isResourceNotFound(error)) throw error;
|
|
9179
7797
|
}
|
|
9180
|
-
await this.client.createTable(
|
|
9181
|
-
AttributeDefinitions: [
|
|
9182
|
-
{
|
|
9183
|
-
AttributeName: "pk",
|
|
9184
|
-
AttributeType: "S"
|
|
9185
|
-
},
|
|
9186
|
-
{
|
|
9187
|
-
AttributeName: "sk",
|
|
9188
|
-
AttributeType: "S"
|
|
9189
|
-
},
|
|
9190
|
-
{
|
|
9191
|
-
AttributeName: "gsi1pk",
|
|
9192
|
-
AttributeType: "S"
|
|
9193
|
-
},
|
|
9194
|
-
{
|
|
9195
|
-
AttributeName: "gsi1sk",
|
|
9196
|
-
AttributeType: "S"
|
|
9197
|
-
}
|
|
9198
|
-
],
|
|
9199
|
-
BillingMode: "PAY_PER_REQUEST",
|
|
9200
|
-
DeletionProtectionEnabled: true,
|
|
9201
|
-
GlobalSecondaryIndexes: [{
|
|
9202
|
-
IndexName: DYNAMODB_UPDATE_INDEX_NAME,
|
|
9203
|
-
KeySchema: [...updateIndexKeySchema],
|
|
9204
|
-
Projection: { ProjectionType: "ALL" },
|
|
9205
|
-
OnDemandThroughput: onDemandThroughput
|
|
9206
|
-
}],
|
|
9207
|
-
KeySchema: [...primaryKeySchema],
|
|
9208
|
-
OnDemandThroughput: onDemandThroughput,
|
|
9209
|
-
TableName: tableName
|
|
9210
|
-
});
|
|
7798
|
+
await this.client.createTable(buildDynamoDBCreateTableInput(tableName));
|
|
9211
7799
|
await (0, _aws_sdk_client_dynamodb.waitUntilTableExists)({
|
|
9212
7800
|
client: this.client,
|
|
9213
7801
|
maxWaitTime: 120
|
|
@@ -9217,14 +7805,66 @@ var DynamoDBManager = class {
|
|
|
9217
7805
|
async ensureLifecycle(tableName) {
|
|
9218
7806
|
const { ContinuousBackupsDescription } = await this.client.describeContinuousBackups({ TableName: tableName });
|
|
9219
7807
|
if (ContinuousBackupsDescription?.PointInTimeRecoveryDescription?.PointInTimeRecoveryStatus === "ENABLED") return;
|
|
9220
|
-
await this.client.updateContinuousBackups(
|
|
9221
|
-
PointInTimeRecoverySpecification: { PointInTimeRecoveryEnabled: true },
|
|
9222
|
-
TableName: tableName
|
|
9223
|
-
});
|
|
7808
|
+
await this.client.updateContinuousBackups(buildDynamoDBBackupInput(tableName));
|
|
9224
7809
|
}
|
|
9225
7810
|
};
|
|
9226
7811
|
//#endregion
|
|
9227
7812
|
//#region iac/iam.ts
|
|
7813
|
+
/** The partitions the plugin's items use: each table's rows, and its index items after `#`. */
|
|
7814
|
+
const dynamoDBLeadingKeys = () => [..._hot_updater_server_database.builtInSchema.tables, _hot_updater_server_database.SETTINGS_TABLE].flatMap(({ name }) => [name, `${name}#*`]);
|
|
7815
|
+
const buildDynamoDBPolicy = (region, accountId, tableName) => {
|
|
7816
|
+
const tableArn = `arn:aws:dynamodb:${region}:${accountId}:table/${tableName}`;
|
|
7817
|
+
return {
|
|
7818
|
+
Version: "2012-10-17",
|
|
7819
|
+
Statement: [{
|
|
7820
|
+
Action: [
|
|
7821
|
+
"dynamodb:BatchGetItem",
|
|
7822
|
+
"dynamodb:ConditionCheckItem",
|
|
7823
|
+
"dynamodb:DeleteItem",
|
|
7824
|
+
"dynamodb:GetItem",
|
|
7825
|
+
"dynamodb:PutItem",
|
|
7826
|
+
"dynamodb:Query",
|
|
7827
|
+
"dynamodb:TransactWriteItems",
|
|
7828
|
+
"dynamodb:UpdateItem"
|
|
7829
|
+
],
|
|
7830
|
+
Condition: { "ForAllValues:StringLike": { "dynamodb:LeadingKeys": dynamoDBLeadingKeys() } },
|
|
7831
|
+
Effect: "Allow",
|
|
7832
|
+
Resource: [tableArn]
|
|
7833
|
+
}]
|
|
7834
|
+
};
|
|
7835
|
+
};
|
|
7836
|
+
const buildS3Policy = (bucketName) => {
|
|
7837
|
+
return {
|
|
7838
|
+
Version: "2012-10-17",
|
|
7839
|
+
Statement: [{
|
|
7840
|
+
Action: ["s3:ListBucket"],
|
|
7841
|
+
Effect: "Allow",
|
|
7842
|
+
Resource: [`arn:aws:s3:::${bucketName}`]
|
|
7843
|
+
}, {
|
|
7844
|
+
Action: ["s3:GetObject"],
|
|
7845
|
+
Effect: "Allow",
|
|
7846
|
+
Resource: [`arn:aws:s3:::${bucketName}/*`]
|
|
7847
|
+
}]
|
|
7848
|
+
};
|
|
7849
|
+
};
|
|
7850
|
+
const buildSsmPolicy = (region, accountId, parameterName) => {
|
|
7851
|
+
return {
|
|
7852
|
+
Version: "2012-10-17",
|
|
7853
|
+
Statement: [{
|
|
7854
|
+
Effect: "Allow",
|
|
7855
|
+
Action: ["ssm:GetParameter"],
|
|
7856
|
+
Resource: `arn:aws:ssm:${region}:${accountId}:parameter/${parameterName.replace(/^\/+/, "")}`
|
|
7857
|
+
}]
|
|
7858
|
+
};
|
|
7859
|
+
};
|
|
7860
|
+
const LAMBDA_EDGE_TRUST_POLICY = {
|
|
7861
|
+
Version: "2012-10-17",
|
|
7862
|
+
Statement: [{
|
|
7863
|
+
Effect: "Allow",
|
|
7864
|
+
Principal: { Service: ["lambda.amazonaws.com", "edgelambda.amazonaws.com"] },
|
|
7865
|
+
Action: "sts:AssumeRole"
|
|
7866
|
+
}]
|
|
7867
|
+
};
|
|
9228
7868
|
var IAMManager = class {
|
|
9229
7869
|
region;
|
|
9230
7870
|
credentials;
|
|
@@ -9241,71 +7881,22 @@ var IAMManager = class {
|
|
|
9241
7881
|
});
|
|
9242
7882
|
}
|
|
9243
7883
|
async ensureDynamoDBPolicy(iamClient, roleName, accountId, tableName) {
|
|
9244
|
-
const tableArn = `arn:aws:dynamodb:${this.region}:${accountId}:table/${tableName}`;
|
|
9245
7884
|
await iamClient.putRolePolicy({
|
|
9246
|
-
PolicyDocument: JSON.stringify(
|
|
9247
|
-
Version: "2012-10-17",
|
|
9248
|
-
Statement: [{
|
|
9249
|
-
Action: ["dynamodb:Query"],
|
|
9250
|
-
Effect: "Allow",
|
|
9251
|
-
Resource: [`${tableArn}/index/${DYNAMODB_UPDATE_INDEX_NAME}`]
|
|
9252
|
-
}, {
|
|
9253
|
-
Action: [
|
|
9254
|
-
"dynamodb:BatchGetItem",
|
|
9255
|
-
"dynamodb:DeleteItem",
|
|
9256
|
-
"dynamodb:GetItem",
|
|
9257
|
-
"dynamodb:PutItem",
|
|
9258
|
-
"dynamodb:Query",
|
|
9259
|
-
"dynamodb:TransactWriteItems",
|
|
9260
|
-
"dynamodb:UpdateItem"
|
|
9261
|
-
],
|
|
9262
|
-
Condition: { "ForAllValues:StringEquals": { "dynamodb:LeadingKeys": [
|
|
9263
|
-
"_hot-updater",
|
|
9264
|
-
"bundles",
|
|
9265
|
-
"bundle_patches",
|
|
9266
|
-
DYNAMODB_CHANNEL_PARTITION,
|
|
9267
|
-
DYNAMODB_CHANNEL_NAME_PARTITION,
|
|
9268
|
-
DYNAMODB_INSIGHTS_PARTITION,
|
|
9269
|
-
DYNAMODB_API_KEY_PARTITION,
|
|
9270
|
-
DYNAMODB_API_KEY_HASH_PARTITION
|
|
9271
|
-
] } },
|
|
9272
|
-
Effect: "Allow",
|
|
9273
|
-
Resource: [tableArn]
|
|
9274
|
-
}]
|
|
9275
|
-
}),
|
|
7885
|
+
PolicyDocument: JSON.stringify(buildDynamoDBPolicy(this.region, accountId, tableName)),
|
|
9276
7886
|
PolicyName: "HotUpdaterDynamoDBReadAccess",
|
|
9277
7887
|
RoleName: roleName
|
|
9278
7888
|
});
|
|
9279
7889
|
}
|
|
9280
7890
|
async ensureS3Policy(iamClient, roleName, bucketName) {
|
|
9281
7891
|
await iamClient.putRolePolicy({
|
|
9282
|
-
PolicyDocument: JSON.stringify(
|
|
9283
|
-
Version: "2012-10-17",
|
|
9284
|
-
Statement: [{
|
|
9285
|
-
Action: ["s3:ListBucket"],
|
|
9286
|
-
Effect: "Allow",
|
|
9287
|
-
Resource: [`arn:aws:s3:::${bucketName}`]
|
|
9288
|
-
}, {
|
|
9289
|
-
Action: ["s3:GetObject"],
|
|
9290
|
-
Effect: "Allow",
|
|
9291
|
-
Resource: [`arn:aws:s3:::${bucketName}/*`]
|
|
9292
|
-
}]
|
|
9293
|
-
}),
|
|
7892
|
+
PolicyDocument: JSON.stringify(buildS3Policy(bucketName)),
|
|
9294
7893
|
PolicyName: "HotUpdaterS3ReadAccess",
|
|
9295
7894
|
RoleName: roleName
|
|
9296
7895
|
});
|
|
9297
7896
|
}
|
|
9298
7897
|
async ensureSsmPolicy(iamClient, roleName, accountId, parameterName) {
|
|
9299
|
-
const parameterPath = parameterName.replace(/^\/+/, "");
|
|
9300
7898
|
await iamClient.putRolePolicy({
|
|
9301
|
-
PolicyDocument: JSON.stringify(
|
|
9302
|
-
Version: "2012-10-17",
|
|
9303
|
-
Statement: [{
|
|
9304
|
-
Effect: "Allow",
|
|
9305
|
-
Action: ["ssm:GetParameter"],
|
|
9306
|
-
Resource: `arn:aws:ssm:${this.region}:${accountId}:parameter/${parameterPath}`
|
|
9307
|
-
}]
|
|
9308
|
-
}),
|
|
7899
|
+
PolicyDocument: JSON.stringify(buildSsmPolicy(this.region, accountId, parameterName)),
|
|
9309
7900
|
PolicyName: "HotUpdaterSSMAccess",
|
|
9310
7901
|
RoleName: roleName
|
|
9311
7902
|
});
|
|
@@ -9320,14 +7911,7 @@ var IAMManager = class {
|
|
|
9320
7911
|
credentials: this.credentials
|
|
9321
7912
|
}).getCallerIdentity({})).Account;
|
|
9322
7913
|
if (!accountId) throw new Error("Failed to get AWS account ID");
|
|
9323
|
-
const assumeRolePolicyDocument = JSON.stringify(
|
|
9324
|
-
Version: "2012-10-17",
|
|
9325
|
-
Statement: [{
|
|
9326
|
-
Effect: "Allow",
|
|
9327
|
-
Principal: { Service: ["lambda.amazonaws.com", "edgelambda.amazonaws.com"] },
|
|
9328
|
-
Action: "sts:AssumeRole"
|
|
9329
|
-
}]
|
|
9330
|
-
});
|
|
7914
|
+
const assumeRolePolicyDocument = JSON.stringify(LAMBDA_EDGE_TRUST_POLICY);
|
|
9331
7915
|
const roleName = `hot-updater-edge-${(0, node_crypto.createHash)("sha256").update(options.lambdaName).digest("hex").slice(0, 16)}`;
|
|
9332
7916
|
try {
|
|
9333
7917
|
const { Role: existingRole } = await iamClient.getRole({ RoleName: roleName });
|
|
@@ -9403,7 +7987,8 @@ var LambdaEdgeDeployer = class {
|
|
|
9403
7987
|
async deploy(lambdaRoleArn, lambdaName, config) {
|
|
9404
7988
|
const cwd = (0, _hot_updater_cli_tools.getCwd)();
|
|
9405
7989
|
const lambdaPath = require.resolve("@hot-updater/aws/lambda");
|
|
9406
|
-
const
|
|
7990
|
+
const lambdaDir = path.default.dirname(lambdaPath);
|
|
7991
|
+
const { tmpDir, removeTmpDir } = await (0, _hot_updater_cli_tools.copyDirToTmp)(lambdaDir);
|
|
9407
7992
|
const indexPath = path.default.join(tmpDir, "index.cjs");
|
|
9408
7993
|
const code = (0, _hot_updater_cli_tools.transformEnv)(indexPath, {
|
|
9409
7994
|
CLOUDFRONT_KEY_PAIR_ID: config.publicKeyId,
|
|
@@ -9636,7 +8221,7 @@ var SSMKeyPairManager = class {
|
|
|
9636
8221
|
_hot_updater_cli_tools.p.log.info("Using existing CloudFront key pair from SSM Parameter Store");
|
|
9637
8222
|
return keyPair;
|
|
9638
8223
|
}
|
|
9639
|
-
const { publicKey, privateKey } = crypto.default.generateKeyPairSync("rsa", {
|
|
8224
|
+
const { publicKey, privateKey } = crypto$1.default.generateKeyPairSync("rsa", {
|
|
9640
8225
|
modulusLength: 2048,
|
|
9641
8226
|
publicKeyEncoding: {
|
|
9642
8227
|
type: "spki",
|
|
@@ -9648,7 +8233,7 @@ var SSMKeyPairManager = class {
|
|
|
9648
8233
|
}
|
|
9649
8234
|
});
|
|
9650
8235
|
const keyPair = {
|
|
9651
|
-
keyPairId: `HOTUPDATER-${crypto.default.randomBytes(4).toString("hex").toUpperCase()}`,
|
|
8236
|
+
keyPairId: `HOTUPDATER-${crypto$1.default.randomBytes(4).toString("hex").toUpperCase()}`,
|
|
9652
8237
|
publicKey,
|
|
9653
8238
|
privateKey
|
|
9654
8239
|
};
|
|
@@ -9711,12 +8296,11 @@ const awsOptions = {
|
|
|
9711
8296
|
};`.trim()
|
|
9712
8297
|
}];
|
|
9713
8298
|
break;
|
|
9714
|
-
case "account":
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
code: `
|
|
8299
|
+
case "account": helperStatements = [{
|
|
8300
|
+
name: "awsOptions",
|
|
8301
|
+
strategy: "merge-object",
|
|
8302
|
+
replaceIncompatibleProperties: ["credentials"],
|
|
8303
|
+
code: `
|
|
9720
8304
|
const awsOptions = {
|
|
9721
8305
|
region: process.env.HOT_UPDATER_S3_REGION!,
|
|
9722
8306
|
credentials: {
|
|
@@ -9724,8 +8308,7 @@ const awsOptions = {
|
|
|
9724
8308
|
secretAccessKey: process.env.HOT_UPDATER_S3_SECRET_ACCESS_KEY!,
|
|
9725
8309
|
},
|
|
9726
8310
|
};`.trim()
|
|
9727
|
-
|
|
9728
|
-
break;
|
|
8311
|
+
}];
|
|
9729
8312
|
}
|
|
9730
8313
|
const builder = new _hot_updater_cli_tools.ConfigBuilder().setBuildType(build).setStorage(storageConfig).setDatabase(databaseConfig).setIntermediateCode(helperStatements.map((statement) => statement.code.trim()).join("\n\n"));
|
|
9731
8314
|
switch (authMode.mode) {
|
|
@@ -9735,13 +8318,10 @@ const awsOptions = {
|
|
|
9735
8318
|
named: ["fromSSO"]
|
|
9736
8319
|
});
|
|
9737
8320
|
break;
|
|
9738
|
-
case "local":
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
});
|
|
9743
|
-
break;
|
|
9744
|
-
case "account": break;
|
|
8321
|
+
case "local": builder.addImport({
|
|
8322
|
+
pkg: "@aws-sdk/credential-providers",
|
|
8323
|
+
named: [authMode.profile ? "fromIni" : "fromNodeProviderChain"]
|
|
8324
|
+
});
|
|
9745
8325
|
}
|
|
9746
8326
|
return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(builder, { helperStatements });
|
|
9747
8327
|
};
|
|
@@ -9776,11 +8356,12 @@ const isAwsRegion = (value) => {
|
|
|
9776
8356
|
return value !== void 0 && Object.hasOwn(regionLocationMap, value);
|
|
9777
8357
|
};
|
|
9778
8358
|
const prepareDynamoDBDeployment = async (input) => {
|
|
9779
|
-
|
|
8359
|
+
await new DynamoDBManager(input.region, input.credentials).ensureTable(input.tableName);
|
|
8360
|
+
await migrateDynamoDB(input);
|
|
9780
8361
|
};
|
|
8362
|
+
/** The app's client key, through the managed server's apiKeys() plugin, on the table it reads. */
|
|
9781
8363
|
const prepareDynamoDBApiKey = async (input) => {
|
|
9782
|
-
return (await (0,
|
|
9783
|
-
apiKeys: input.apiKeys,
|
|
8364
|
+
return (await (0, _hot_updater_server_db.createDatabasePluginApis)(input.database, plugins).apiKeys.provision({
|
|
9784
8365
|
existingApiKey: input.existingApiKey,
|
|
9785
8366
|
name: "AWS init"
|
|
9786
8367
|
})).apiKey;
|
|
@@ -9905,13 +8486,14 @@ const runInit = async ({ build, envFile }) => {
|
|
|
9905
8486
|
dynamodbTableName: resolvedDynamoDBTableName,
|
|
9906
8487
|
lambdaName
|
|
9907
8488
|
};
|
|
8489
|
+
const persistCredentialInputs = await (0, _hot_updater_cli_tools.confirmInitInputPersistence)({
|
|
8490
|
+
existingEnv: managedEnv,
|
|
8491
|
+
inputs: resolvedInputs,
|
|
8492
|
+
nonInteractive,
|
|
8493
|
+
provider: initProvider
|
|
8494
|
+
});
|
|
9908
8495
|
const initEnv = (0, _hot_updater_cli_tools.getInitProviderEnvVars)({
|
|
9909
|
-
includeConsentInputs:
|
|
9910
|
-
existingEnv: managedEnv,
|
|
9911
|
-
inputs: resolvedInputs,
|
|
9912
|
-
nonInteractive,
|
|
9913
|
-
provider: initProvider
|
|
9914
|
-
}),
|
|
8496
|
+
includeConsentInputs: persistCredentialInputs,
|
|
9915
8497
|
inputs: resolvedInputs,
|
|
9916
8498
|
provider: initProvider
|
|
9917
8499
|
});
|
|
@@ -9943,7 +8525,7 @@ const runInit = async ({ build, envFile }) => {
|
|
|
9943
8525
|
let apiKey;
|
|
9944
8526
|
try {
|
|
9945
8527
|
apiKey = await prepareDynamoDBApiKey({
|
|
9946
|
-
|
|
8528
|
+
database: databasePlugin,
|
|
9947
8529
|
existingApiKey: providerEnv.HOT_UPDATER_API_KEY
|
|
9948
8530
|
});
|
|
9949
8531
|
await (0, _hot_updater_cli_tools.makeEnv)({ HOT_UPDATER_API_KEY: apiKey });
|
|
@@ -9990,6 +8572,7 @@ const runInit = async ({ build, envFile }) => {
|
|
|
9990
8572
|
if (configWriteResult.status === "created") _hot_updater_cli_tools.p.log.success("Generated 'hot-updater.config.ts' file with AWS settings.");
|
|
9991
8573
|
else if (configWriteResult.status === "merged") _hot_updater_cli_tools.p.log.success("Updated 'hot-updater.config.ts' file with AWS settings.");
|
|
9992
8574
|
else _hot_updater_cli_tools.p.log.warn(`Kept existing 'hot-updater.config.ts' unchanged: ${configWriteResult.reason}`);
|
|
8575
|
+
await (0, _hot_updater_cli_tools.generateHotUpdaterPlugins)("@hot-updater/aws");
|
|
9993
8576
|
const sourceUrl = `https://${distributionDomain}`;
|
|
9994
8577
|
_hot_updater_cli_tools.p.note((0, _hot_updater_cli_tools.transformTemplate)(SOURCE_TEMPLATE, {
|
|
9995
8578
|
apiKey: JSON.stringify(apiKey),
|
|
@@ -10001,6 +8584,13 @@ const runInit = async ({ build, envFile }) => {
|
|
|
10001
8584
|
_hot_updater_cli_tools.p.log.success("Done! 🎉");
|
|
10002
8585
|
};
|
|
10003
8586
|
//#endregion
|
|
8587
|
+
exports.LAMBDA_EDGE_TRUST_POLICY = LAMBDA_EDGE_TRUST_POLICY;
|
|
8588
|
+
exports.buildDynamoDBBackupInput = buildDynamoDBBackupInput;
|
|
8589
|
+
exports.buildDynamoDBCreateTableInput = buildDynamoDBCreateTableInput;
|
|
8590
|
+
exports.buildDynamoDBPolicy = buildDynamoDBPolicy;
|
|
8591
|
+
exports.buildDynamoDBSchemaSettingsInput = buildDynamoDBSchemaSettingsInput;
|
|
8592
|
+
exports.buildS3Policy = buildS3Policy;
|
|
8593
|
+
exports.buildSsmPolicy = buildSsmPolicy;
|
|
10004
8594
|
exports.prepareDynamoDBApiKey = prepareDynamoDBApiKey;
|
|
10005
8595
|
exports.prepareDynamoDBDeployment = prepareDynamoDBDeployment;
|
|
10006
8596
|
exports.runInit = runInit;
|