@arcote.tech/arc-cli 0.7.0 → 0.7.1

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/index.js CHANGED
@@ -8974,547 +8974,826 @@ var require_chokidar = __commonJS((exports) => {
8974
8974
  exports.watch = watch;
8975
8975
  });
8976
8976
 
8977
- // ../../node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
8978
- var require_src = __commonJS((exports, module) => {
8979
- var ESC2 = "\x1B";
8980
- var CSI = `${ESC2}[`;
8981
- var beep2 = "\x07";
8982
- var cursor = {
8983
- to(x, y) {
8984
- if (!y)
8985
- return `${CSI}${x + 1}G`;
8986
- return `${CSI}${y + 1};${x + 1}H`;
8987
- },
8988
- move(x, y) {
8989
- let ret = "";
8990
- if (x < 0)
8991
- ret += `${CSI}${-x}D`;
8992
- else if (x > 0)
8993
- ret += `${CSI}${x}C`;
8994
- if (y < 0)
8995
- ret += `${CSI}${-y}A`;
8996
- else if (y > 0)
8997
- ret += `${CSI}${y}B`;
8998
- return ret;
8999
- },
9000
- up: (count = 1) => `${CSI}${count}A`,
9001
- down: (count = 1) => `${CSI}${count}B`,
9002
- forward: (count = 1) => `${CSI}${count}C`,
9003
- backward: (count = 1) => `${CSI}${count}D`,
9004
- nextLine: (count = 1) => `${CSI}E`.repeat(count),
9005
- prevLine: (count = 1) => `${CSI}F`.repeat(count),
9006
- left: `${CSI}G`,
9007
- hide: `${CSI}?25l`,
9008
- show: `${CSI}?25h`,
9009
- save: `${ESC2}7`,
9010
- restore: `${ESC2}8`
9011
- };
9012
- var scroll = {
9013
- up: (count = 1) => `${CSI}S`.repeat(count),
9014
- down: (count = 1) => `${CSI}T`.repeat(count)
9015
- };
9016
- var erase = {
9017
- screen: `${CSI}2J`,
9018
- up: (count = 1) => `${CSI}1J`.repeat(count),
9019
- down: (count = 1) => `${CSI}J`.repeat(count),
9020
- line: `${CSI}2K`,
9021
- lineEnd: `${CSI}K`,
9022
- lineStart: `${CSI}1K`,
9023
- lines(count) {
9024
- let clear = "";
9025
- for (let i = 0;i < count; i++)
9026
- clear += this.line + (i < count - 1 ? cursor.up() : "");
9027
- if (count)
9028
- clear += cursor.left;
9029
- return clear;
8977
+ // src/i18n/catalog.ts
8978
+ function hashMsgid(msgid) {
8979
+ const hasher = new Bun.CryptoHasher("md5");
8980
+ hasher.update(msgid);
8981
+ return hasher.digest("hex").slice(0, 8);
8982
+ }
8983
+ function parsePo(content) {
8984
+ const entries = [];
8985
+ const lines = content.split(`
8986
+ `);
8987
+ let locations = [];
8988
+ let hash = "";
8989
+ let msgid = "";
8990
+ let msgstr = "";
8991
+ let obsolete = false;
8992
+ let inManual = false;
8993
+ const flush = () => {
8994
+ if (msgid) {
8995
+ entries.push({
8996
+ msgid,
8997
+ msgstr,
8998
+ locations,
8999
+ hash: hash || hashMsgid(msgid),
9000
+ obsolete,
9001
+ ...inManual ? { manual: true } : {}
9002
+ });
9030
9003
  }
9004
+ locations = [];
9005
+ hash = "";
9006
+ msgid = "";
9007
+ msgstr = "";
9008
+ obsolete = false;
9031
9009
  };
9032
- module.exports = { cursor, scroll, erase, beep: beep2 };
9033
- });
9034
-
9035
- // ../../node_modules/.bun/picocolors@1.1.1/node_modules/picocolors/picocolors.js
9036
- var require_picocolors = __commonJS((exports, module) => {
9037
- var p = process || {};
9038
- var argv2 = p.argv || [];
9039
- var env2 = p.env || {};
9040
- var isColorSupported2 = !(!!env2.NO_COLOR || argv2.includes("--no-color")) && (!!env2.FORCE_COLOR || argv2.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env2.TERM !== "dumb" || !!env2.CI);
9041
- var formatter = (open, close, replace = open) => (input) => {
9042
- let string = "" + input, index = string.indexOf(close, open.length);
9043
- return ~index ? open + replaceClose2(string, close, replace, index) + close : open + string + close;
9044
- };
9045
- var replaceClose2 = (string, close, replace, index) => {
9046
- let result = "", cursor = 0;
9047
- do {
9048
- result += string.substring(cursor, index) + replace;
9049
- cursor = index + close.length;
9050
- index = string.indexOf(close, cursor);
9051
- } while (~index);
9052
- return result + string.substring(cursor);
9053
- };
9054
- var createColors2 = (enabled = isColorSupported2) => {
9055
- let f = enabled ? formatter : () => String;
9056
- return {
9057
- isColorSupported: enabled,
9058
- reset: f("\x1B[0m", "\x1B[0m"),
9059
- bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
9060
- dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
9061
- italic: f("\x1B[3m", "\x1B[23m"),
9062
- underline: f("\x1B[4m", "\x1B[24m"),
9063
- inverse: f("\x1B[7m", "\x1B[27m"),
9064
- hidden: f("\x1B[8m", "\x1B[28m"),
9065
- strikethrough: f("\x1B[9m", "\x1B[29m"),
9066
- black: f("\x1B[30m", "\x1B[39m"),
9067
- red: f("\x1B[31m", "\x1B[39m"),
9068
- green: f("\x1B[32m", "\x1B[39m"),
9069
- yellow: f("\x1B[33m", "\x1B[39m"),
9070
- blue: f("\x1B[34m", "\x1B[39m"),
9071
- magenta: f("\x1B[35m", "\x1B[39m"),
9072
- cyan: f("\x1B[36m", "\x1B[39m"),
9073
- white: f("\x1B[37m", "\x1B[39m"),
9074
- gray: f("\x1B[90m", "\x1B[39m"),
9075
- bgBlack: f("\x1B[40m", "\x1B[49m"),
9076
- bgRed: f("\x1B[41m", "\x1B[49m"),
9077
- bgGreen: f("\x1B[42m", "\x1B[49m"),
9078
- bgYellow: f("\x1B[43m", "\x1B[49m"),
9079
- bgBlue: f("\x1B[44m", "\x1B[49m"),
9080
- bgMagenta: f("\x1B[45m", "\x1B[49m"),
9081
- bgCyan: f("\x1B[46m", "\x1B[49m"),
9082
- bgWhite: f("\x1B[47m", "\x1B[49m"),
9083
- blackBright: f("\x1B[90m", "\x1B[39m"),
9084
- redBright: f("\x1B[91m", "\x1B[39m"),
9085
- greenBright: f("\x1B[92m", "\x1B[39m"),
9086
- yellowBright: f("\x1B[93m", "\x1B[39m"),
9087
- blueBright: f("\x1B[94m", "\x1B[39m"),
9088
- magentaBright: f("\x1B[95m", "\x1B[39m"),
9089
- cyanBright: f("\x1B[96m", "\x1B[39m"),
9090
- whiteBright: f("\x1B[97m", "\x1B[39m"),
9091
- bgBlackBright: f("\x1B[100m", "\x1B[49m"),
9092
- bgRedBright: f("\x1B[101m", "\x1B[49m"),
9093
- bgGreenBright: f("\x1B[102m", "\x1B[49m"),
9094
- bgYellowBright: f("\x1B[103m", "\x1B[49m"),
9095
- bgBlueBright: f("\x1B[104m", "\x1B[49m"),
9096
- bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
9097
- bgCyanBright: f("\x1B[106m", "\x1B[49m"),
9098
- bgWhiteBright: f("\x1B[107m", "\x1B[49m")
9099
- };
9100
- };
9101
- module.exports = createColors2();
9102
- module.exports.createColors = createColors2;
9103
- });
9104
-
9105
- // ../../node_modules/.bun/safe-buffer@5.2.1/node_modules/safe-buffer/index.js
9106
- var require_safe_buffer = __commonJS((exports, module) => {
9107
- /*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
9108
- var buffer = __require("buffer");
9109
- var Buffer2 = buffer.Buffer;
9110
- function copyProps(src, dst) {
9111
- for (var key in src) {
9112
- dst[key] = src[key];
9113
- }
9114
- }
9115
- if (Buffer2.from && Buffer2.alloc && Buffer2.allocUnsafe && Buffer2.allocUnsafeSlow) {
9116
- module.exports = buffer;
9117
- } else {
9118
- copyProps(buffer, exports);
9119
- exports.Buffer = SafeBuffer;
9120
- }
9121
- function SafeBuffer(arg, encodingOrOffset, length) {
9122
- return Buffer2(arg, encodingOrOffset, length);
9123
- }
9124
- SafeBuffer.prototype = Object.create(Buffer2.prototype);
9125
- copyProps(Buffer2, SafeBuffer);
9126
- SafeBuffer.from = function(arg, encodingOrOffset, length) {
9127
- if (typeof arg === "number") {
9128
- throw new TypeError("Argument must not be a number");
9010
+ for (const line of lines) {
9011
+ const trimmed = line.trim();
9012
+ if (trimmed === "# manual") {
9013
+ inManual = true;
9014
+ continue;
9129
9015
  }
9130
- return Buffer2(arg, encodingOrOffset, length);
9131
- };
9132
- SafeBuffer.alloc = function(size, fill, encoding) {
9133
- if (typeof size !== "number") {
9134
- throw new TypeError("Argument must be a number");
9016
+ if (trimmed === "# end manual") {
9017
+ if (msgid)
9018
+ flush();
9019
+ inManual = false;
9020
+ continue;
9135
9021
  }
9136
- var buf = Buffer2(size);
9137
- if (fill !== undefined) {
9138
- if (typeof encoding === "string") {
9139
- buf.fill(fill, encoding);
9140
- } else {
9141
- buf.fill(fill);
9142
- }
9143
- } else {
9144
- buf.fill(0);
9022
+ if (trimmed === "" || trimmed.startsWith("#,")) {
9023
+ if (msgid)
9024
+ flush();
9025
+ continue;
9145
9026
  }
9146
- return buf;
9147
- };
9148
- SafeBuffer.allocUnsafe = function(size) {
9149
- if (typeof size !== "number") {
9150
- throw new TypeError("Argument must be a number");
9027
+ if (trimmed.startsWith("#:")) {
9028
+ if (msgid)
9029
+ flush();
9030
+ locations.push(trimmed.slice(3).trim());
9031
+ continue;
9151
9032
  }
9152
- return Buffer2(size);
9153
- };
9154
- SafeBuffer.allocUnsafeSlow = function(size) {
9155
- if (typeof size !== "number") {
9156
- throw new TypeError("Argument must be a number");
9033
+ if (trimmed.startsWith("#.")) {
9034
+ const hashMatch = trimmed.match(/hash:(\w+)/);
9035
+ if (hashMatch)
9036
+ hash = hashMatch[1];
9037
+ continue;
9157
9038
  }
9158
- return buffer.SlowBuffer(size);
9159
- };
9160
- });
9161
-
9162
- // ../../node_modules/.bun/jws@4.0.1/node_modules/jws/lib/data-stream.js
9163
- var require_data_stream = __commonJS((exports, module) => {
9164
- var Buffer2 = require_safe_buffer().Buffer;
9165
- var Stream2 = __require("stream");
9166
- var util = __require("util");
9167
- function DataStream(data) {
9168
- this.buffer = null;
9169
- this.writable = true;
9170
- this.readable = true;
9171
- if (!data) {
9172
- this.buffer = Buffer2.alloc(0);
9173
- return this;
9039
+ if (trimmed.startsWith("#~")) {
9040
+ const rest = trimmed.slice(3).trim();
9041
+ if (rest.startsWith("msgid")) {
9042
+ if (msgid)
9043
+ flush();
9044
+ obsolete = true;
9045
+ msgid = extractQuoted(rest.slice(5));
9046
+ } else if (rest.startsWith("msgstr")) {
9047
+ msgstr = extractQuoted(rest.slice(6));
9048
+ }
9049
+ continue;
9174
9050
  }
9175
- if (typeof data.pipe === "function") {
9176
- this.buffer = Buffer2.alloc(0);
9177
- data.pipe(this);
9178
- return this;
9051
+ if (trimmed.startsWith("msgid")) {
9052
+ if (msgid)
9053
+ flush();
9054
+ msgid = extractQuoted(trimmed.slice(5));
9055
+ } else if (trimmed.startsWith("msgstr")) {
9056
+ msgstr = extractQuoted(trimmed.slice(6));
9179
9057
  }
9180
- if (data.length || typeof data === "object") {
9181
- this.buffer = data;
9182
- this.writable = false;
9183
- process.nextTick(function() {
9184
- this.emit("end", data);
9185
- this.readable = false;
9186
- this.emit("close");
9187
- }.bind(this));
9188
- return this;
9058
+ }
9059
+ flush();
9060
+ return entries;
9061
+ }
9062
+ function writePo(entries) {
9063
+ const lines = [];
9064
+ const manual = entries.filter((e) => e.manual);
9065
+ const active = entries.filter((e) => !e.obsolete && !e.manual);
9066
+ const obsolete = entries.filter((e) => e.obsolete);
9067
+ if (manual.length > 0) {
9068
+ lines.push("# manual");
9069
+ for (const entry of manual) {
9070
+ lines.push(`msgid ${quoteString(entry.msgid)}`);
9071
+ lines.push(`msgstr ${quoteString(entry.msgstr)}`);
9072
+ lines.push("");
9189
9073
  }
9190
- throw new TypeError("Unexpected data type (" + typeof data + ")");
9074
+ lines.push("# end manual");
9075
+ lines.push("");
9191
9076
  }
9192
- util.inherits(DataStream, Stream2);
9193
- DataStream.prototype.write = function write(data) {
9194
- this.buffer = Buffer2.concat([this.buffer, Buffer2.from(data)]);
9195
- this.emit("data", data);
9196
- };
9197
- DataStream.prototype.end = function end(data) {
9198
- if (data)
9199
- this.write(data);
9200
- this.emit("end", data);
9201
- this.emit("close");
9202
- this.writable = false;
9203
- this.readable = false;
9204
- };
9205
- module.exports = DataStream;
9206
- });
9207
-
9208
- // ../../node_modules/.bun/ecdsa-sig-formatter@1.0.11/node_modules/ecdsa-sig-formatter/src/param-bytes-for-alg.js
9209
- var require_param_bytes_for_alg = __commonJS((exports, module) => {
9210
- function getParamSize(keySize) {
9211
- var result = (keySize / 8 | 0) + (keySize % 8 === 0 ? 0 : 1);
9212
- return result;
9077
+ for (const entry of active) {
9078
+ for (const loc of entry.locations) {
9079
+ lines.push(`#: ${loc}`);
9080
+ }
9081
+ lines.push(`#. hash:${entry.hash}`);
9082
+ lines.push(`msgid ${quoteString(entry.msgid)}`);
9083
+ lines.push(`msgstr ${quoteString(entry.msgstr)}`);
9084
+ lines.push("");
9213
9085
  }
9214
- var paramBytesForAlg = {
9215
- ES256: getParamSize(256),
9216
- ES384: getParamSize(384),
9217
- ES512: getParamSize(521)
9218
- };
9219
- function getParamBytesForAlg(alg) {
9220
- var paramBytes = paramBytesForAlg[alg];
9221
- if (paramBytes) {
9222
- return paramBytes;
9086
+ if (obsolete.length > 0) {
9087
+ lines.push("# Obsolete entries");
9088
+ lines.push("");
9089
+ for (const entry of obsolete) {
9090
+ lines.push(`#~ msgid ${quoteString(entry.msgid)}`);
9091
+ lines.push(`#~ msgstr ${quoteString(entry.msgstr)}`);
9092
+ lines.push("");
9223
9093
  }
9224
- throw new Error('Unknown algorithm "' + alg + '"');
9225
9094
  }
9226
- module.exports = getParamBytesForAlg;
9227
- });
9228
-
9229
- // ../../node_modules/.bun/ecdsa-sig-formatter@1.0.11/node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.js
9230
- var require_ecdsa_sig_formatter = __commonJS((exports, module) => {
9231
- var Buffer2 = require_safe_buffer().Buffer;
9232
- var getParamBytesForAlg = require_param_bytes_for_alg();
9233
- var MAX_OCTET = 128;
9234
- var CLASS_UNIVERSAL = 0;
9235
- var PRIMITIVE_BIT = 32;
9236
- var TAG_SEQ = 16;
9237
- var TAG_INT = 2;
9238
- var ENCODED_TAG_SEQ = TAG_SEQ | PRIMITIVE_BIT | CLASS_UNIVERSAL << 6;
9239
- var ENCODED_TAG_INT = TAG_INT | CLASS_UNIVERSAL << 6;
9240
- function base64Url(base64) {
9241
- return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
9095
+ return lines.join(`
9096
+ `);
9097
+ }
9098
+ function mergeCatalog(existing, extracted) {
9099
+ const existingMap = new Map;
9100
+ for (const entry of existing) {
9101
+ existingMap.set(entry.msgid, entry);
9242
9102
  }
9243
- function signatureAsBuffer(signature) {
9244
- if (Buffer2.isBuffer(signature)) {
9245
- return signature;
9246
- } else if (typeof signature === "string") {
9247
- return Buffer2.from(signature, "base64");
9248
- }
9249
- throw new TypeError("ECDSA signature must be a Base64 string or a Buffer");
9103
+ const manualEntries = existing.filter((e) => e.manual);
9104
+ const manualIds = new Set(manualEntries.map((e) => e.msgid));
9105
+ const result = [];
9106
+ const seen = new Set;
9107
+ for (const [msgid, locations] of extracted) {
9108
+ seen.add(msgid);
9109
+ if (manualIds.has(msgid))
9110
+ continue;
9111
+ const prev = existingMap.get(msgid);
9112
+ result.push({
9113
+ msgid,
9114
+ msgstr: prev?.msgstr ?? "",
9115
+ locations: [...locations].sort(),
9116
+ hash: hashMsgid(msgid),
9117
+ obsolete: false
9118
+ });
9250
9119
  }
9251
- function derToJose(signature, alg) {
9252
- signature = signatureAsBuffer(signature);
9253
- var paramBytes = getParamBytesForAlg(alg);
9254
- var maxEncodedParamLength = paramBytes + 1;
9255
- var inputLength = signature.length;
9256
- var offset = 0;
9257
- if (signature[offset++] !== ENCODED_TAG_SEQ) {
9258
- throw new Error('Could not find expected "seq"');
9259
- }
9260
- var seqLength = signature[offset++];
9261
- if (seqLength === (MAX_OCTET | 1)) {
9262
- seqLength = signature[offset++];
9263
- }
9264
- if (inputLength - offset < seqLength) {
9265
- throw new Error('"seq" specified length of "' + seqLength + '", only "' + (inputLength - offset) + '" remaining');
9266
- }
9267
- if (signature[offset++] !== ENCODED_TAG_INT) {
9268
- throw new Error('Could not find expected "int" for "r"');
9269
- }
9270
- var rLength = signature[offset++];
9271
- if (inputLength - offset - 2 < rLength) {
9272
- throw new Error('"r" specified length of "' + rLength + '", only "' + (inputLength - offset - 2) + '" available');
9273
- }
9274
- if (maxEncodedParamLength < rLength) {
9275
- throw new Error('"r" specified length of "' + rLength + '", max of "' + maxEncodedParamLength + '" is acceptable');
9276
- }
9277
- var rOffset = offset;
9278
- offset += rLength;
9279
- if (signature[offset++] !== ENCODED_TAG_INT) {
9280
- throw new Error('Could not find expected "int" for "s"');
9281
- }
9282
- var sLength = signature[offset++];
9283
- if (inputLength - offset !== sLength) {
9284
- throw new Error('"s" specified length of "' + sLength + '", expected "' + (inputLength - offset) + '"');
9285
- }
9286
- if (maxEncodedParamLength < sLength) {
9287
- throw new Error('"s" specified length of "' + sLength + '", max of "' + maxEncodedParamLength + '" is acceptable');
9288
- }
9289
- var sOffset = offset;
9290
- offset += sLength;
9291
- if (offset !== inputLength) {
9292
- throw new Error('Expected to consume entire buffer, but "' + (inputLength - offset) + '" bytes remain');
9293
- }
9294
- var rPadding = paramBytes - rLength, sPadding = paramBytes - sLength;
9295
- var dst = Buffer2.allocUnsafe(rPadding + rLength + sPadding + sLength);
9296
- for (offset = 0;offset < rPadding; ++offset) {
9297
- dst[offset] = 0;
9298
- }
9299
- signature.copy(dst, offset, rOffset + Math.max(-rPadding, 0), rOffset + rLength);
9300
- offset = paramBytes;
9301
- for (var o = offset;offset < o + sPadding; ++offset) {
9302
- dst[offset] = 0;
9120
+ for (const entry of existing) {
9121
+ if (!seen.has(entry.msgid) && !entry.obsolete && !entry.manual && entry.msgstr) {
9122
+ result.push({
9123
+ ...entry,
9124
+ obsolete: true,
9125
+ locations: []
9126
+ });
9303
9127
  }
9304
- signature.copy(dst, offset, sOffset + Math.max(-sPadding, 0), sOffset + sLength);
9305
- dst = dst.toString("base64");
9306
- dst = base64Url(dst);
9307
- return dst;
9308
9128
  }
9309
- function countPadding(buf, start, stop) {
9310
- var padding = 0;
9311
- while (start + padding < stop && buf[start + padding] === 0) {
9312
- ++padding;
9313
- }
9314
- var needsSign = buf[start + padding] >= MAX_OCTET;
9315
- if (needsSign) {
9316
- --padding;
9317
- }
9318
- return padding;
9129
+ result.sort((a, b) => a.msgid.localeCompare(b.msgid));
9130
+ return [...manualEntries, ...result];
9131
+ }
9132
+ function extractQuoted(s) {
9133
+ const trimmed = s.trim();
9134
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
9135
+ return trimmed.slice(1, -1).replace(/\\n/g, `
9136
+ `).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
9319
9137
  }
9320
- function joseToDer(signature, alg) {
9321
- signature = signatureAsBuffer(signature);
9322
- var paramBytes = getParamBytesForAlg(alg);
9323
- var signatureBytes = signature.length;
9324
- if (signatureBytes !== paramBytes * 2) {
9325
- throw new TypeError('"' + alg + '" signatures must be "' + paramBytes * 2 + '" bytes, saw "' + signatureBytes + '"');
9326
- }
9327
- var rPadding = countPadding(signature, 0, paramBytes);
9328
- var sPadding = countPadding(signature, paramBytes, signature.length);
9329
- var rLength = paramBytes - rPadding;
9330
- var sLength = paramBytes - sPadding;
9331
- var rsBytes = 1 + 1 + rLength + 1 + 1 + sLength;
9332
- var shortLength = rsBytes < MAX_OCTET;
9333
- var dst = Buffer2.allocUnsafe((shortLength ? 2 : 3) + rsBytes);
9334
- var offset = 0;
9335
- dst[offset++] = ENCODED_TAG_SEQ;
9336
- if (shortLength) {
9337
- dst[offset++] = rsBytes;
9338
- } else {
9339
- dst[offset++] = MAX_OCTET | 1;
9340
- dst[offset++] = rsBytes & 255;
9341
- }
9342
- dst[offset++] = ENCODED_TAG_INT;
9343
- dst[offset++] = rLength;
9344
- if (rPadding < 0) {
9345
- dst[offset++] = 0;
9346
- offset += signature.copy(dst, offset, 0, paramBytes);
9347
- } else {
9348
- offset += signature.copy(dst, offset, rPadding, paramBytes);
9138
+ return trimmed;
9139
+ }
9140
+ function quoteString(s) {
9141
+ const escaped = s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
9142
+ return `"${escaped}"`;
9143
+ }
9144
+
9145
+ // src/i18n/compile.ts
9146
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
9147
+ import { join as join4 } from "path";
9148
+ function compileCatalog(poPath) {
9149
+ const content = readFileSync3(poPath, "utf-8");
9150
+ const entries = parsePo(content);
9151
+ const result = {};
9152
+ for (const entry of entries) {
9153
+ if (!entry.obsolete && entry.msgstr) {
9154
+ result[entry.msgid] = entry.msgstr;
9349
9155
  }
9350
- dst[offset++] = ENCODED_TAG_INT;
9351
- dst[offset++] = sLength;
9352
- if (sPadding < 0) {
9353
- dst[offset++] = 0;
9354
- signature.copy(dst, offset, paramBytes);
9355
- } else {
9356
- signature.copy(dst, offset, paramBytes + sPadding);
9357
- }
9358
- return dst;
9359
9156
  }
9360
- module.exports = {
9361
- derToJose,
9362
- joseToDer
9157
+ const sorted = {};
9158
+ for (const key of Object.keys(result).sort()) {
9159
+ sorted[key] = result[key];
9160
+ }
9161
+ return sorted;
9162
+ }
9163
+ function compileAllCatalogs(localesDir, outDir) {
9164
+ mkdirSync3(outDir, { recursive: true });
9165
+ for (const file of readdirSync2(localesDir)) {
9166
+ if (!file.endsWith(".po"))
9167
+ continue;
9168
+ const locale = file.replace(".po", "");
9169
+ const compiled = compileCatalog(join4(localesDir, file));
9170
+ writeFileSync3(join4(outDir, `${locale}.json`), JSON.stringify(compiled));
9171
+ }
9172
+ }
9173
+ var init_compile = () => {};
9174
+
9175
+ // src/i18n/plugin.ts
9176
+ function i18nExtractPlugin(collector, rootDir) {
9177
+ return {
9178
+ name: "arc-i18n-extract",
9179
+ setup(build2) {
9180
+ build2.onLoad({ filter: /\.tsx?$/ }, async (args) => {
9181
+ const source = await Bun.file(args.path).text();
9182
+ const relPath = args.path.startsWith(rootDir) ? args.path.slice(rootDir.length + 1) : args.path;
9183
+ const transRegex = /<Trans>\s*([^<{]+?)\s*<\/Trans>/g;
9184
+ for (const match2 of source.matchAll(transRegex)) {
9185
+ const msgid = match2[1].trim();
9186
+ if (!msgid)
9187
+ continue;
9188
+ const line = source.substring(0, match2.index).split(`
9189
+ `).length;
9190
+ const loc = `${relPath}:${line}`;
9191
+ if (!collector.has(msgid))
9192
+ collector.set(msgid, new Set);
9193
+ collector.get(msgid).add(loc);
9194
+ }
9195
+ const tRegex = /\bt`([^`]+)`/g;
9196
+ for (const match2 of source.matchAll(tRegex)) {
9197
+ const msgid = match2[1];
9198
+ if (!msgid)
9199
+ continue;
9200
+ const line = source.substring(0, match2.index).split(`
9201
+ `).length;
9202
+ const loc = `${relPath}:${line}`;
9203
+ if (!collector.has(msgid))
9204
+ collector.set(msgid, new Set);
9205
+ collector.get(msgid).add(loc);
9206
+ }
9207
+ return;
9208
+ });
9209
+ }
9363
9210
  };
9211
+ }
9212
+
9213
+ // src/i18n/index.ts
9214
+ var exports_i18n = {};
9215
+ __export(exports_i18n, {
9216
+ readTranslationsConfig: () => readTranslationsConfig,
9217
+ i18nExtractPlugin: () => i18nExtractPlugin,
9218
+ finalizeTranslations: () => finalizeTranslations
9219
+ });
9220
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
9221
+ import { dirname as dirname4, join as join5 } from "path";
9222
+ function readTranslationsConfig(rootDir) {
9223
+ const pkgPath = join5(rootDir, "package.json");
9224
+ if (!existsSync4(pkgPath))
9225
+ return null;
9226
+ const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
9227
+ const config = pkg.arc?.translations;
9228
+ if (!config?.locales?.length)
9229
+ return null;
9230
+ return {
9231
+ locales: config.locales,
9232
+ sourceLocale: config.sourceLocale ?? config.locales[0]
9233
+ };
9234
+ }
9235
+ async function finalizeTranslations(rootDir, outDir, collector) {
9236
+ const config = readTranslationsConfig(rootDir);
9237
+ if (!config || collector.size === 0)
9238
+ return;
9239
+ const localesJsonDir = join5(outDir, "locales");
9240
+ mkdirSync4(localesJsonDir, { recursive: true });
9241
+ console.log(` Extracted ${collector.size} translatable string(s) for ${config.locales.length} locale(s)`);
9242
+ for (const locale of config.locales) {
9243
+ const poPath = join5(rootDir, "locales", `${locale}.po`);
9244
+ mkdirSync4(dirname4(poPath), { recursive: true });
9245
+ const existing = existsSync4(poPath) ? parsePo(readFileSync4(poPath, "utf-8")) : [];
9246
+ const merged = mergeCatalog(existing, collector);
9247
+ writeFileSync4(poPath, writePo(merged));
9248
+ const compiled = compileCatalog(poPath);
9249
+ writeFileSync4(join5(localesJsonDir, `${locale}.json`), JSON.stringify(compiled));
9250
+ }
9251
+ }
9252
+ var init_i18n = __esm(() => {
9253
+ init_compile();
9364
9254
  });
9365
9255
 
9366
- // ../../node_modules/.bun/buffer-equal-constant-time@1.0.1/node_modules/buffer-equal-constant-time/index.js
9367
- var require_buffer_equal_constant_time = __commonJS((exports, module) => {
9368
- var Buffer2 = __require("buffer").Buffer;
9369
- var SlowBuffer = __require("buffer").SlowBuffer;
9370
- module.exports = bufferEq;
9371
- function bufferEq(a2, b2) {
9372
- if (!Buffer2.isBuffer(a2) || !Buffer2.isBuffer(b2)) {
9373
- return false;
9374
- }
9375
- if (a2.length !== b2.length) {
9376
- return false;
9377
- }
9378
- var c2 = 0;
9379
- for (var i = 0;i < a2.length; i++) {
9380
- c2 |= a2[i] ^ b2[i];
9256
+ // ../../node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
9257
+ var require_src = __commonJS((exports, module) => {
9258
+ var ESC2 = "\x1B";
9259
+ var CSI = `${ESC2}[`;
9260
+ var beep2 = "\x07";
9261
+ var cursor = {
9262
+ to(x, y) {
9263
+ if (!y)
9264
+ return `${CSI}${x + 1}G`;
9265
+ return `${CSI}${y + 1};${x + 1}H`;
9266
+ },
9267
+ move(x, y) {
9268
+ let ret = "";
9269
+ if (x < 0)
9270
+ ret += `${CSI}${-x}D`;
9271
+ else if (x > 0)
9272
+ ret += `${CSI}${x}C`;
9273
+ if (y < 0)
9274
+ ret += `${CSI}${-y}A`;
9275
+ else if (y > 0)
9276
+ ret += `${CSI}${y}B`;
9277
+ return ret;
9278
+ },
9279
+ up: (count = 1) => `${CSI}${count}A`,
9280
+ down: (count = 1) => `${CSI}${count}B`,
9281
+ forward: (count = 1) => `${CSI}${count}C`,
9282
+ backward: (count = 1) => `${CSI}${count}D`,
9283
+ nextLine: (count = 1) => `${CSI}E`.repeat(count),
9284
+ prevLine: (count = 1) => `${CSI}F`.repeat(count),
9285
+ left: `${CSI}G`,
9286
+ hide: `${CSI}?25l`,
9287
+ show: `${CSI}?25h`,
9288
+ save: `${ESC2}7`,
9289
+ restore: `${ESC2}8`
9290
+ };
9291
+ var scroll = {
9292
+ up: (count = 1) => `${CSI}S`.repeat(count),
9293
+ down: (count = 1) => `${CSI}T`.repeat(count)
9294
+ };
9295
+ var erase = {
9296
+ screen: `${CSI}2J`,
9297
+ up: (count = 1) => `${CSI}1J`.repeat(count),
9298
+ down: (count = 1) => `${CSI}J`.repeat(count),
9299
+ line: `${CSI}2K`,
9300
+ lineEnd: `${CSI}K`,
9301
+ lineStart: `${CSI}1K`,
9302
+ lines(count) {
9303
+ let clear = "";
9304
+ for (let i = 0;i < count; i++)
9305
+ clear += this.line + (i < count - 1 ? cursor.up() : "");
9306
+ if (count)
9307
+ clear += cursor.left;
9308
+ return clear;
9381
9309
  }
9382
- return c2 === 0;
9383
- }
9384
- bufferEq.install = function() {
9385
- Buffer2.prototype.equal = SlowBuffer.prototype.equal = function equal(that) {
9386
- return bufferEq(this, that);
9387
- };
9388
9310
  };
9389
- var origBufEqual = Buffer2.prototype.equal;
9390
- var origSlowBufEqual = SlowBuffer.prototype.equal;
9391
- bufferEq.restore = function() {
9392
- Buffer2.prototype.equal = origBufEqual;
9393
- SlowBuffer.prototype.equal = origSlowBufEqual;
9311
+ module.exports = { cursor, scroll, erase, beep: beep2 };
9312
+ });
9313
+
9314
+ // ../../node_modules/.bun/picocolors@1.1.1/node_modules/picocolors/picocolors.js
9315
+ var require_picocolors = __commonJS((exports, module) => {
9316
+ var p = process || {};
9317
+ var argv2 = p.argv || [];
9318
+ var env2 = p.env || {};
9319
+ var isColorSupported2 = !(!!env2.NO_COLOR || argv2.includes("--no-color")) && (!!env2.FORCE_COLOR || argv2.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env2.TERM !== "dumb" || !!env2.CI);
9320
+ var formatter = (open, close, replace = open) => (input) => {
9321
+ let string = "" + input, index = string.indexOf(close, open.length);
9322
+ return ~index ? open + replaceClose2(string, close, replace, index) + close : open + string + close;
9323
+ };
9324
+ var replaceClose2 = (string, close, replace, index) => {
9325
+ let result = "", cursor = 0;
9326
+ do {
9327
+ result += string.substring(cursor, index) + replace;
9328
+ cursor = index + close.length;
9329
+ index = string.indexOf(close, cursor);
9330
+ } while (~index);
9331
+ return result + string.substring(cursor);
9332
+ };
9333
+ var createColors2 = (enabled = isColorSupported2) => {
9334
+ let f = enabled ? formatter : () => String;
9335
+ return {
9336
+ isColorSupported: enabled,
9337
+ reset: f("\x1B[0m", "\x1B[0m"),
9338
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
9339
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
9340
+ italic: f("\x1B[3m", "\x1B[23m"),
9341
+ underline: f("\x1B[4m", "\x1B[24m"),
9342
+ inverse: f("\x1B[7m", "\x1B[27m"),
9343
+ hidden: f("\x1B[8m", "\x1B[28m"),
9344
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
9345
+ black: f("\x1B[30m", "\x1B[39m"),
9346
+ red: f("\x1B[31m", "\x1B[39m"),
9347
+ green: f("\x1B[32m", "\x1B[39m"),
9348
+ yellow: f("\x1B[33m", "\x1B[39m"),
9349
+ blue: f("\x1B[34m", "\x1B[39m"),
9350
+ magenta: f("\x1B[35m", "\x1B[39m"),
9351
+ cyan: f("\x1B[36m", "\x1B[39m"),
9352
+ white: f("\x1B[37m", "\x1B[39m"),
9353
+ gray: f("\x1B[90m", "\x1B[39m"),
9354
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
9355
+ bgRed: f("\x1B[41m", "\x1B[49m"),
9356
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
9357
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
9358
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
9359
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
9360
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
9361
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
9362
+ blackBright: f("\x1B[90m", "\x1B[39m"),
9363
+ redBright: f("\x1B[91m", "\x1B[39m"),
9364
+ greenBright: f("\x1B[92m", "\x1B[39m"),
9365
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
9366
+ blueBright: f("\x1B[94m", "\x1B[39m"),
9367
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
9368
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
9369
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
9370
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
9371
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
9372
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
9373
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
9374
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
9375
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
9376
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
9377
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
9378
+ };
9394
9379
  };
9380
+ module.exports = createColors2();
9381
+ module.exports.createColors = createColors2;
9395
9382
  });
9396
9383
 
9397
- // ../../node_modules/.bun/jwa@2.0.1/node_modules/jwa/index.js
9398
- var require_jwa = __commonJS((exports, module) => {
9399
- var Buffer2 = require_safe_buffer().Buffer;
9400
- var crypto2 = __require("crypto");
9401
- var formatEcdsa = require_ecdsa_sig_formatter();
9402
- var util = __require("util");
9403
- var MSG_INVALID_ALGORITHM = `"%s" is not a valid algorithm.
9404
- Supported algorithms are:
9405
- "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".`;
9406
- var MSG_INVALID_SECRET = "secret must be a string or buffer";
9407
- var MSG_INVALID_VERIFIER_KEY = "key must be a string or a buffer";
9408
- var MSG_INVALID_SIGNER_KEY = "key must be a string, a buffer or an object";
9409
- var supportsKeyObjects = typeof crypto2.createPublicKey === "function";
9410
- if (supportsKeyObjects) {
9411
- MSG_INVALID_VERIFIER_KEY += " or a KeyObject";
9412
- MSG_INVALID_SECRET += "or a KeyObject";
9384
+ // ../../node_modules/.bun/safe-buffer@5.2.1/node_modules/safe-buffer/index.js
9385
+ var require_safe_buffer = __commonJS((exports, module) => {
9386
+ /*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
9387
+ var buffer = __require("buffer");
9388
+ var Buffer2 = buffer.Buffer;
9389
+ function copyProps(src, dst) {
9390
+ for (var key in src) {
9391
+ dst[key] = src[key];
9392
+ }
9413
9393
  }
9414
- function checkIsPublicKey(key) {
9415
- if (Buffer2.isBuffer(key)) {
9416
- return;
9394
+ if (Buffer2.from && Buffer2.alloc && Buffer2.allocUnsafe && Buffer2.allocUnsafeSlow) {
9395
+ module.exports = buffer;
9396
+ } else {
9397
+ copyProps(buffer, exports);
9398
+ exports.Buffer = SafeBuffer;
9399
+ }
9400
+ function SafeBuffer(arg, encodingOrOffset, length) {
9401
+ return Buffer2(arg, encodingOrOffset, length);
9402
+ }
9403
+ SafeBuffer.prototype = Object.create(Buffer2.prototype);
9404
+ copyProps(Buffer2, SafeBuffer);
9405
+ SafeBuffer.from = function(arg, encodingOrOffset, length) {
9406
+ if (typeof arg === "number") {
9407
+ throw new TypeError("Argument must not be a number");
9417
9408
  }
9418
- if (typeof key === "string") {
9419
- return;
9409
+ return Buffer2(arg, encodingOrOffset, length);
9410
+ };
9411
+ SafeBuffer.alloc = function(size, fill, encoding) {
9412
+ if (typeof size !== "number") {
9413
+ throw new TypeError("Argument must be a number");
9420
9414
  }
9421
- if (!supportsKeyObjects) {
9422
- throw typeError(MSG_INVALID_VERIFIER_KEY);
9415
+ var buf = Buffer2(size);
9416
+ if (fill !== undefined) {
9417
+ if (typeof encoding === "string") {
9418
+ buf.fill(fill, encoding);
9419
+ } else {
9420
+ buf.fill(fill);
9421
+ }
9422
+ } else {
9423
+ buf.fill(0);
9423
9424
  }
9424
- if (typeof key !== "object") {
9425
- throw typeError(MSG_INVALID_VERIFIER_KEY);
9425
+ return buf;
9426
+ };
9427
+ SafeBuffer.allocUnsafe = function(size) {
9428
+ if (typeof size !== "number") {
9429
+ throw new TypeError("Argument must be a number");
9426
9430
  }
9427
- if (typeof key.type !== "string") {
9428
- throw typeError(MSG_INVALID_VERIFIER_KEY);
9431
+ return Buffer2(size);
9432
+ };
9433
+ SafeBuffer.allocUnsafeSlow = function(size) {
9434
+ if (typeof size !== "number") {
9435
+ throw new TypeError("Argument must be a number");
9429
9436
  }
9430
- if (typeof key.asymmetricKeyType !== "string") {
9431
- throw typeError(MSG_INVALID_VERIFIER_KEY);
9437
+ return buffer.SlowBuffer(size);
9438
+ };
9439
+ });
9440
+
9441
+ // ../../node_modules/.bun/jws@4.0.1/node_modules/jws/lib/data-stream.js
9442
+ var require_data_stream = __commonJS((exports, module) => {
9443
+ var Buffer2 = require_safe_buffer().Buffer;
9444
+ var Stream2 = __require("stream");
9445
+ var util = __require("util");
9446
+ function DataStream(data) {
9447
+ this.buffer = null;
9448
+ this.writable = true;
9449
+ this.readable = true;
9450
+ if (!data) {
9451
+ this.buffer = Buffer2.alloc(0);
9452
+ return this;
9432
9453
  }
9433
- if (typeof key.export !== "function") {
9434
- throw typeError(MSG_INVALID_VERIFIER_KEY);
9454
+ if (typeof data.pipe === "function") {
9455
+ this.buffer = Buffer2.alloc(0);
9456
+ data.pipe(this);
9457
+ return this;
9435
9458
  }
9436
- }
9437
- function checkIsPrivateKey(key) {
9438
- if (Buffer2.isBuffer(key)) {
9439
- return;
9459
+ if (data.length || typeof data === "object") {
9460
+ this.buffer = data;
9461
+ this.writable = false;
9462
+ process.nextTick(function() {
9463
+ this.emit("end", data);
9464
+ this.readable = false;
9465
+ this.emit("close");
9466
+ }.bind(this));
9467
+ return this;
9440
9468
  }
9441
- if (typeof key === "string") {
9442
- return;
9469
+ throw new TypeError("Unexpected data type (" + typeof data + ")");
9470
+ }
9471
+ util.inherits(DataStream, Stream2);
9472
+ DataStream.prototype.write = function write(data) {
9473
+ this.buffer = Buffer2.concat([this.buffer, Buffer2.from(data)]);
9474
+ this.emit("data", data);
9475
+ };
9476
+ DataStream.prototype.end = function end(data) {
9477
+ if (data)
9478
+ this.write(data);
9479
+ this.emit("end", data);
9480
+ this.emit("close");
9481
+ this.writable = false;
9482
+ this.readable = false;
9483
+ };
9484
+ module.exports = DataStream;
9485
+ });
9486
+
9487
+ // ../../node_modules/.bun/ecdsa-sig-formatter@1.0.11/node_modules/ecdsa-sig-formatter/src/param-bytes-for-alg.js
9488
+ var require_param_bytes_for_alg = __commonJS((exports, module) => {
9489
+ function getParamSize(keySize) {
9490
+ var result = (keySize / 8 | 0) + (keySize % 8 === 0 ? 0 : 1);
9491
+ return result;
9492
+ }
9493
+ var paramBytesForAlg = {
9494
+ ES256: getParamSize(256),
9495
+ ES384: getParamSize(384),
9496
+ ES512: getParamSize(521)
9497
+ };
9498
+ function getParamBytesForAlg(alg) {
9499
+ var paramBytes = paramBytesForAlg[alg];
9500
+ if (paramBytes) {
9501
+ return paramBytes;
9443
9502
  }
9444
- if (typeof key === "object") {
9445
- return;
9503
+ throw new Error('Unknown algorithm "' + alg + '"');
9504
+ }
9505
+ module.exports = getParamBytesForAlg;
9506
+ });
9507
+
9508
+ // ../../node_modules/.bun/ecdsa-sig-formatter@1.0.11/node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.js
9509
+ var require_ecdsa_sig_formatter = __commonJS((exports, module) => {
9510
+ var Buffer2 = require_safe_buffer().Buffer;
9511
+ var getParamBytesForAlg = require_param_bytes_for_alg();
9512
+ var MAX_OCTET = 128;
9513
+ var CLASS_UNIVERSAL = 0;
9514
+ var PRIMITIVE_BIT = 32;
9515
+ var TAG_SEQ = 16;
9516
+ var TAG_INT = 2;
9517
+ var ENCODED_TAG_SEQ = TAG_SEQ | PRIMITIVE_BIT | CLASS_UNIVERSAL << 6;
9518
+ var ENCODED_TAG_INT = TAG_INT | CLASS_UNIVERSAL << 6;
9519
+ function base64Url(base64) {
9520
+ return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
9521
+ }
9522
+ function signatureAsBuffer(signature) {
9523
+ if (Buffer2.isBuffer(signature)) {
9524
+ return signature;
9525
+ } else if (typeof signature === "string") {
9526
+ return Buffer2.from(signature, "base64");
9446
9527
  }
9447
- throw typeError(MSG_INVALID_SIGNER_KEY);
9528
+ throw new TypeError("ECDSA signature must be a Base64 string or a Buffer");
9448
9529
  }
9449
- function checkIsSecretKey(key) {
9450
- if (Buffer2.isBuffer(key)) {
9451
- return;
9530
+ function derToJose(signature, alg) {
9531
+ signature = signatureAsBuffer(signature);
9532
+ var paramBytes = getParamBytesForAlg(alg);
9533
+ var maxEncodedParamLength = paramBytes + 1;
9534
+ var inputLength = signature.length;
9535
+ var offset = 0;
9536
+ if (signature[offset++] !== ENCODED_TAG_SEQ) {
9537
+ throw new Error('Could not find expected "seq"');
9452
9538
  }
9453
- if (typeof key === "string") {
9454
- return key;
9539
+ var seqLength = signature[offset++];
9540
+ if (seqLength === (MAX_OCTET | 1)) {
9541
+ seqLength = signature[offset++];
9455
9542
  }
9456
- if (!supportsKeyObjects) {
9457
- throw typeError(MSG_INVALID_SECRET);
9543
+ if (inputLength - offset < seqLength) {
9544
+ throw new Error('"seq" specified length of "' + seqLength + '", only "' + (inputLength - offset) + '" remaining');
9458
9545
  }
9459
- if (typeof key !== "object") {
9460
- throw typeError(MSG_INVALID_SECRET);
9546
+ if (signature[offset++] !== ENCODED_TAG_INT) {
9547
+ throw new Error('Could not find expected "int" for "r"');
9461
9548
  }
9462
- if (key.type !== "secret") {
9463
- throw typeError(MSG_INVALID_SECRET);
9549
+ var rLength = signature[offset++];
9550
+ if (inputLength - offset - 2 < rLength) {
9551
+ throw new Error('"r" specified length of "' + rLength + '", only "' + (inputLength - offset - 2) + '" available');
9464
9552
  }
9465
- if (typeof key.export !== "function") {
9466
- throw typeError(MSG_INVALID_SECRET);
9553
+ if (maxEncodedParamLength < rLength) {
9554
+ throw new Error('"r" specified length of "' + rLength + '", max of "' + maxEncodedParamLength + '" is acceptable');
9467
9555
  }
9468
- }
9469
- function fromBase64(base64) {
9470
- return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
9471
- }
9472
- function toBase64(base64url) {
9473
- base64url = base64url.toString();
9474
- var padding = 4 - base64url.length % 4;
9475
- if (padding !== 4) {
9476
- for (var i = 0;i < padding; ++i) {
9477
- base64url += "=";
9478
- }
9556
+ var rOffset = offset;
9557
+ offset += rLength;
9558
+ if (signature[offset++] !== ENCODED_TAG_INT) {
9559
+ throw new Error('Could not find expected "int" for "s"');
9479
9560
  }
9480
- return base64url.replace(/\-/g, "+").replace(/_/g, "/");
9481
- }
9482
- function typeError(template) {
9483
- var args = [].slice.call(arguments, 1);
9484
- var errMsg = util.format.bind(util, template).apply(null, args);
9485
- return new TypeError(errMsg);
9486
- }
9487
- function bufferOrString(obj) {
9488
- return Buffer2.isBuffer(obj) || typeof obj === "string";
9489
- }
9490
- function normalizeInput(thing) {
9491
- if (!bufferOrString(thing))
9492
- thing = JSON.stringify(thing);
9493
- return thing;
9494
- }
9495
- function createHmacSigner(bits) {
9496
- return function sign(thing, secret) {
9497
- checkIsSecretKey(secret);
9498
- thing = normalizeInput(thing);
9499
- var hmac = crypto2.createHmac("sha" + bits, secret);
9500
- var sig = (hmac.update(thing), hmac.digest("base64"));
9501
- return fromBase64(sig);
9502
- };
9503
- }
9504
- var bufferEqual;
9505
- var timingSafeEqual = "timingSafeEqual" in crypto2 ? function timingSafeEqual2(a2, b2) {
9506
- if (a2.byteLength !== b2.byteLength) {
9507
- return false;
9561
+ var sLength = signature[offset++];
9562
+ if (inputLength - offset !== sLength) {
9563
+ throw new Error('"s" specified length of "' + sLength + '", expected "' + (inputLength - offset) + '"');
9508
9564
  }
9509
- return crypto2.timingSafeEqual(a2, b2);
9510
- } : function timingSafeEqual2(a2, b2) {
9511
- if (!bufferEqual) {
9512
- bufferEqual = require_buffer_equal_constant_time();
9565
+ if (maxEncodedParamLength < sLength) {
9566
+ throw new Error('"s" specified length of "' + sLength + '", max of "' + maxEncodedParamLength + '" is acceptable');
9513
9567
  }
9514
- return bufferEqual(a2, b2);
9515
- };
9516
- function createHmacVerifier(bits) {
9517
- return function verify(thing, signature, secret) {
9568
+ var sOffset = offset;
9569
+ offset += sLength;
9570
+ if (offset !== inputLength) {
9571
+ throw new Error('Expected to consume entire buffer, but "' + (inputLength - offset) + '" bytes remain');
9572
+ }
9573
+ var rPadding = paramBytes - rLength, sPadding = paramBytes - sLength;
9574
+ var dst = Buffer2.allocUnsafe(rPadding + rLength + sPadding + sLength);
9575
+ for (offset = 0;offset < rPadding; ++offset) {
9576
+ dst[offset] = 0;
9577
+ }
9578
+ signature.copy(dst, offset, rOffset + Math.max(-rPadding, 0), rOffset + rLength);
9579
+ offset = paramBytes;
9580
+ for (var o = offset;offset < o + sPadding; ++offset) {
9581
+ dst[offset] = 0;
9582
+ }
9583
+ signature.copy(dst, offset, sOffset + Math.max(-sPadding, 0), sOffset + sLength);
9584
+ dst = dst.toString("base64");
9585
+ dst = base64Url(dst);
9586
+ return dst;
9587
+ }
9588
+ function countPadding(buf, start, stop) {
9589
+ var padding = 0;
9590
+ while (start + padding < stop && buf[start + padding] === 0) {
9591
+ ++padding;
9592
+ }
9593
+ var needsSign = buf[start + padding] >= MAX_OCTET;
9594
+ if (needsSign) {
9595
+ --padding;
9596
+ }
9597
+ return padding;
9598
+ }
9599
+ function joseToDer(signature, alg) {
9600
+ signature = signatureAsBuffer(signature);
9601
+ var paramBytes = getParamBytesForAlg(alg);
9602
+ var signatureBytes = signature.length;
9603
+ if (signatureBytes !== paramBytes * 2) {
9604
+ throw new TypeError('"' + alg + '" signatures must be "' + paramBytes * 2 + '" bytes, saw "' + signatureBytes + '"');
9605
+ }
9606
+ var rPadding = countPadding(signature, 0, paramBytes);
9607
+ var sPadding = countPadding(signature, paramBytes, signature.length);
9608
+ var rLength = paramBytes - rPadding;
9609
+ var sLength = paramBytes - sPadding;
9610
+ var rsBytes = 1 + 1 + rLength + 1 + 1 + sLength;
9611
+ var shortLength = rsBytes < MAX_OCTET;
9612
+ var dst = Buffer2.allocUnsafe((shortLength ? 2 : 3) + rsBytes);
9613
+ var offset = 0;
9614
+ dst[offset++] = ENCODED_TAG_SEQ;
9615
+ if (shortLength) {
9616
+ dst[offset++] = rsBytes;
9617
+ } else {
9618
+ dst[offset++] = MAX_OCTET | 1;
9619
+ dst[offset++] = rsBytes & 255;
9620
+ }
9621
+ dst[offset++] = ENCODED_TAG_INT;
9622
+ dst[offset++] = rLength;
9623
+ if (rPadding < 0) {
9624
+ dst[offset++] = 0;
9625
+ offset += signature.copy(dst, offset, 0, paramBytes);
9626
+ } else {
9627
+ offset += signature.copy(dst, offset, rPadding, paramBytes);
9628
+ }
9629
+ dst[offset++] = ENCODED_TAG_INT;
9630
+ dst[offset++] = sLength;
9631
+ if (sPadding < 0) {
9632
+ dst[offset++] = 0;
9633
+ signature.copy(dst, offset, paramBytes);
9634
+ } else {
9635
+ signature.copy(dst, offset, paramBytes + sPadding);
9636
+ }
9637
+ return dst;
9638
+ }
9639
+ module.exports = {
9640
+ derToJose,
9641
+ joseToDer
9642
+ };
9643
+ });
9644
+
9645
+ // ../../node_modules/.bun/buffer-equal-constant-time@1.0.1/node_modules/buffer-equal-constant-time/index.js
9646
+ var require_buffer_equal_constant_time = __commonJS((exports, module) => {
9647
+ var Buffer2 = __require("buffer").Buffer;
9648
+ var SlowBuffer = __require("buffer").SlowBuffer;
9649
+ module.exports = bufferEq;
9650
+ function bufferEq(a2, b2) {
9651
+ if (!Buffer2.isBuffer(a2) || !Buffer2.isBuffer(b2)) {
9652
+ return false;
9653
+ }
9654
+ if (a2.length !== b2.length) {
9655
+ return false;
9656
+ }
9657
+ var c2 = 0;
9658
+ for (var i = 0;i < a2.length; i++) {
9659
+ c2 |= a2[i] ^ b2[i];
9660
+ }
9661
+ return c2 === 0;
9662
+ }
9663
+ bufferEq.install = function() {
9664
+ Buffer2.prototype.equal = SlowBuffer.prototype.equal = function equal(that) {
9665
+ return bufferEq(this, that);
9666
+ };
9667
+ };
9668
+ var origBufEqual = Buffer2.prototype.equal;
9669
+ var origSlowBufEqual = SlowBuffer.prototype.equal;
9670
+ bufferEq.restore = function() {
9671
+ Buffer2.prototype.equal = origBufEqual;
9672
+ SlowBuffer.prototype.equal = origSlowBufEqual;
9673
+ };
9674
+ });
9675
+
9676
+ // ../../node_modules/.bun/jwa@2.0.1/node_modules/jwa/index.js
9677
+ var require_jwa = __commonJS((exports, module) => {
9678
+ var Buffer2 = require_safe_buffer().Buffer;
9679
+ var crypto2 = __require("crypto");
9680
+ var formatEcdsa = require_ecdsa_sig_formatter();
9681
+ var util = __require("util");
9682
+ var MSG_INVALID_ALGORITHM = `"%s" is not a valid algorithm.
9683
+ Supported algorithms are:
9684
+ "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".`;
9685
+ var MSG_INVALID_SECRET = "secret must be a string or buffer";
9686
+ var MSG_INVALID_VERIFIER_KEY = "key must be a string or a buffer";
9687
+ var MSG_INVALID_SIGNER_KEY = "key must be a string, a buffer or an object";
9688
+ var supportsKeyObjects = typeof crypto2.createPublicKey === "function";
9689
+ if (supportsKeyObjects) {
9690
+ MSG_INVALID_VERIFIER_KEY += " or a KeyObject";
9691
+ MSG_INVALID_SECRET += "or a KeyObject";
9692
+ }
9693
+ function checkIsPublicKey(key) {
9694
+ if (Buffer2.isBuffer(key)) {
9695
+ return;
9696
+ }
9697
+ if (typeof key === "string") {
9698
+ return;
9699
+ }
9700
+ if (!supportsKeyObjects) {
9701
+ throw typeError(MSG_INVALID_VERIFIER_KEY);
9702
+ }
9703
+ if (typeof key !== "object") {
9704
+ throw typeError(MSG_INVALID_VERIFIER_KEY);
9705
+ }
9706
+ if (typeof key.type !== "string") {
9707
+ throw typeError(MSG_INVALID_VERIFIER_KEY);
9708
+ }
9709
+ if (typeof key.asymmetricKeyType !== "string") {
9710
+ throw typeError(MSG_INVALID_VERIFIER_KEY);
9711
+ }
9712
+ if (typeof key.export !== "function") {
9713
+ throw typeError(MSG_INVALID_VERIFIER_KEY);
9714
+ }
9715
+ }
9716
+ function checkIsPrivateKey(key) {
9717
+ if (Buffer2.isBuffer(key)) {
9718
+ return;
9719
+ }
9720
+ if (typeof key === "string") {
9721
+ return;
9722
+ }
9723
+ if (typeof key === "object") {
9724
+ return;
9725
+ }
9726
+ throw typeError(MSG_INVALID_SIGNER_KEY);
9727
+ }
9728
+ function checkIsSecretKey(key) {
9729
+ if (Buffer2.isBuffer(key)) {
9730
+ return;
9731
+ }
9732
+ if (typeof key === "string") {
9733
+ return key;
9734
+ }
9735
+ if (!supportsKeyObjects) {
9736
+ throw typeError(MSG_INVALID_SECRET);
9737
+ }
9738
+ if (typeof key !== "object") {
9739
+ throw typeError(MSG_INVALID_SECRET);
9740
+ }
9741
+ if (key.type !== "secret") {
9742
+ throw typeError(MSG_INVALID_SECRET);
9743
+ }
9744
+ if (typeof key.export !== "function") {
9745
+ throw typeError(MSG_INVALID_SECRET);
9746
+ }
9747
+ }
9748
+ function fromBase64(base64) {
9749
+ return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
9750
+ }
9751
+ function toBase64(base64url) {
9752
+ base64url = base64url.toString();
9753
+ var padding = 4 - base64url.length % 4;
9754
+ if (padding !== 4) {
9755
+ for (var i = 0;i < padding; ++i) {
9756
+ base64url += "=";
9757
+ }
9758
+ }
9759
+ return base64url.replace(/\-/g, "+").replace(/_/g, "/");
9760
+ }
9761
+ function typeError(template) {
9762
+ var args = [].slice.call(arguments, 1);
9763
+ var errMsg = util.format.bind(util, template).apply(null, args);
9764
+ return new TypeError(errMsg);
9765
+ }
9766
+ function bufferOrString(obj) {
9767
+ return Buffer2.isBuffer(obj) || typeof obj === "string";
9768
+ }
9769
+ function normalizeInput(thing) {
9770
+ if (!bufferOrString(thing))
9771
+ thing = JSON.stringify(thing);
9772
+ return thing;
9773
+ }
9774
+ function createHmacSigner(bits) {
9775
+ return function sign(thing, secret) {
9776
+ checkIsSecretKey(secret);
9777
+ thing = normalizeInput(thing);
9778
+ var hmac = crypto2.createHmac("sha" + bits, secret);
9779
+ var sig = (hmac.update(thing), hmac.digest("base64"));
9780
+ return fromBase64(sig);
9781
+ };
9782
+ }
9783
+ var bufferEqual;
9784
+ var timingSafeEqual = "timingSafeEqual" in crypto2 ? function timingSafeEqual2(a2, b2) {
9785
+ if (a2.byteLength !== b2.byteLength) {
9786
+ return false;
9787
+ }
9788
+ return crypto2.timingSafeEqual(a2, b2);
9789
+ } : function timingSafeEqual2(a2, b2) {
9790
+ if (!bufferEqual) {
9791
+ bufferEqual = require_buffer_equal_constant_time();
9792
+ }
9793
+ return bufferEqual(a2, b2);
9794
+ };
9795
+ function createHmacVerifier(bits) {
9796
+ return function verify(thing, signature, secret) {
9518
9797
  var computedSig = createHmacSigner(bits)(thing, secret);
9519
9798
  return timingSafeEqual(Buffer2.from(signature), Buffer2.from(computedSig));
9520
9799
  };
@@ -9744,9 +10023,9 @@ var require_verify_stream = __commonJS((exports, module) => {
9744
10023
  }
9745
10024
  function jwsVerify(jwsSig, algorithm, secretOrKey) {
9746
10025
  if (!algorithm) {
9747
- var err2 = new Error("Missing algorithm parameter for jws.verify");
9748
- err2.code = "MISSING_ALGORITHM";
9749
- throw err2;
10026
+ var err3 = new Error("Missing algorithm parameter for jws.verify");
10027
+ err3.code = "MISSING_ALGORITHM";
10028
+ throw err3;
9750
10029
  }
9751
10030
  jwsSig = toString(jwsSig);
9752
10031
  var signature = signatureFromJWS(jwsSig);
@@ -11934,9 +12213,9 @@ var require_verify = __commonJS((exports, module) => {
11934
12213
  if (callback) {
11935
12214
  done = callback;
11936
12215
  } else {
11937
- done = function(err2, data) {
11938
- if (err2)
11939
- throw err2;
12216
+ done = function(err3, data) {
12217
+ if (err3)
12218
+ throw err3;
11940
12219
  return data;
11941
12220
  };
11942
12221
  }
@@ -11963,8 +12242,8 @@ var require_verify = __commonJS((exports, module) => {
11963
12242
  let decodedToken;
11964
12243
  try {
11965
12244
  decodedToken = decode(jwtString, { complete: true });
11966
- } catch (err2) {
11967
- return done(err2);
12245
+ } catch (err3) {
12246
+ return done(err3);
11968
12247
  }
11969
12248
  if (!decodedToken) {
11970
12249
  return done(new JsonWebTokenError("invalid token"));
@@ -11981,9 +12260,9 @@ var require_verify = __commonJS((exports, module) => {
11981
12260
  return secretCallback(null, secretOrPublicKey);
11982
12261
  };
11983
12262
  }
11984
- return getSecret(header, function(err2, secretOrPublicKey2) {
11985
- if (err2) {
11986
- return done(new JsonWebTokenError("error in secret or public key callback: " + err2.message));
12263
+ return getSecret(header, function(err3, secretOrPublicKey2) {
12264
+ if (err3) {
12265
+ return done(new JsonWebTokenError("error in secret or public key callback: " + err3.message));
11987
12266
  }
11988
12267
  const hasSignature = parts[2].trim() !== "";
11989
12268
  if (!hasSignature && secretOrPublicKey2) {
@@ -12619,11 +12898,11 @@ var require_sign = __commonJS((exports, module) => {
12619
12898
  typ: isObjectPayload ? "JWT" : undefined,
12620
12899
  kid: options.keyid
12621
12900
  }, options.header);
12622
- function failure(err2) {
12901
+ function failure(err3) {
12623
12902
  if (callback) {
12624
- return callback(err2);
12903
+ return callback(err3);
12625
12904
  }
12626
- throw err2;
12905
+ throw err3;
12627
12906
  }
12628
12907
  if (!secretOrPrivateKey && options.algorithm !== "none") {
12629
12908
  return failure(new Error("secretOrPrivateKey must have a value"));
@@ -12695,8 +12974,8 @@ var require_sign = __commonJS((exports, module) => {
12695
12974
  if (typeof options.notBefore !== "undefined") {
12696
12975
  try {
12697
12976
  payload.nbf = timespan(options.notBefore, timestamp2);
12698
- } catch (err2) {
12699
- return failure(err2);
12977
+ } catch (err3) {
12978
+ return failure(err3);
12700
12979
  }
12701
12980
  if (typeof payload.nbf === "undefined") {
12702
12981
  return failure(new Error('"notBefore" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'));
@@ -12705,8 +12984,8 @@ var require_sign = __commonJS((exports, module) => {
12705
12984
  if (typeof options.expiresIn !== "undefined" && typeof payload === "object") {
12706
12985
  try {
12707
12986
  payload.exp = timespan(options.expiresIn, timestamp2);
12708
- } catch (err2) {
12709
- return failure(err2);
12987
+ } catch (err3) {
12988
+ return failure(err3);
12710
12989
  }
12711
12990
  if (typeof payload.exp === "undefined") {
12712
12991
  return failure(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'));
@@ -13764,8 +14043,8 @@ class EventWire {
13764
14043
  try {
13765
14044
  const message = JSON.parse(event.data);
13766
14045
  this.handleMessage(message);
13767
- } catch (err2) {
13768
- console.error("EventWire: Failed to parse message", err2);
14046
+ } catch (err3) {
14047
+ console.error("EventWire: Failed to parse message", err3);
13769
14048
  }
13770
14049
  };
13771
14050
  this.ws.onclose = (event) => {
@@ -13774,17 +14053,17 @@ class EventWire {
13774
14053
  console.log("EventWire disconnected");
13775
14054
  this.scheduleReconnect();
13776
14055
  };
13777
- this.ws.onerror = (err2) => {
13778
- console.error("EventWire error:", err2);
14056
+ this.ws.onerror = (err3) => {
14057
+ console.error("EventWire error:", err3);
13779
14058
  if (this.state === "connecting") {
13780
14059
  this.state = "disconnected";
13781
14060
  this.ws = null;
13782
14061
  }
13783
14062
  };
13784
- } catch (err2) {
14063
+ } catch (err3) {
13785
14064
  this.state = "disconnected";
13786
14065
  this.ws = null;
13787
- console.error("EventWire: Failed to connect", err2);
14066
+ console.error("EventWire: Failed to connect", err3);
13788
14067
  this.scheduleReconnect();
13789
14068
  }
13790
14069
  }
@@ -26312,325 +26591,56 @@ ${colors3.yellow}Type declaration errors:${colors3.reset}`);
26312
26591
  rebuildsInProgress.delete(key);
26313
26592
  }
26314
26593
  })();
26315
- rebuildsInProgress.set(key, rebuildPromise);
26316
- await rebuildPromise;
26317
- }
26318
- });
26319
- devContexts.push({
26320
- configPath,
26321
- config,
26322
- configDir,
26323
- contexts,
26324
- watchers: [watcher]
26325
- });
26326
- console.log("");
26327
- log(`Watching for changes in ${configDir}...`, "cyan");
26328
- }
26329
- console.log("");
26330
- log("Development mode started. Press Ctrl+C to stop.", "green");
26331
- process.on("SIGINT", () => {
26332
- console.log("");
26333
- log("Shutting down dev mode...", "yellow");
26334
- for (const devCtx of devContexts) {
26335
- for (const watcher of devCtx.watchers) {
26336
- watcher.close();
26337
- }
26338
- }
26339
- process.exit(0);
26340
- });
26341
- } catch (err) {
26342
- console.error("Error in dev command:", err);
26343
- process.exit(1);
26344
- }
26345
- }
26346
-
26347
- // src/platform/shared.ts
26348
- import { copyFileSync, existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync10, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync9 } from "fs";
26349
- import { dirname as dirname7, join as join11 } from "path";
26350
-
26351
- // src/builder/module-builder.ts
26352
- import { execSync } from "child_process";
26353
- import {
26354
- existsSync as existsSync7,
26355
- mkdirSync as mkdirSync6,
26356
- readFileSync as readFileSync7,
26357
- readdirSync as readdirSync4,
26358
- rmSync,
26359
- writeFileSync as writeFileSync6
26360
- } from "fs";
26361
- import { basename as basename2, dirname as dirname5, join as join8, relative as relative3 } from "path";
26362
-
26363
- // src/i18n/index.ts
26364
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
26365
- import { dirname as dirname4, join as join5 } from "path";
26366
-
26367
- // src/i18n/catalog.ts
26368
- function hashMsgid(msgid) {
26369
- const hasher = new Bun.CryptoHasher("md5");
26370
- hasher.update(msgid);
26371
- return hasher.digest("hex").slice(0, 8);
26372
- }
26373
- function parsePo(content) {
26374
- const entries = [];
26375
- const lines = content.split(`
26376
- `);
26377
- let locations = [];
26378
- let hash = "";
26379
- let msgid = "";
26380
- let msgstr = "";
26381
- let obsolete = false;
26382
- let inManual = false;
26383
- const flush = () => {
26384
- if (msgid) {
26385
- entries.push({
26386
- msgid,
26387
- msgstr,
26388
- locations,
26389
- hash: hash || hashMsgid(msgid),
26390
- obsolete,
26391
- ...inManual ? { manual: true } : {}
26392
- });
26393
- }
26394
- locations = [];
26395
- hash = "";
26396
- msgid = "";
26397
- msgstr = "";
26398
- obsolete = false;
26399
- };
26400
- for (const line of lines) {
26401
- const trimmed = line.trim();
26402
- if (trimmed === "# manual") {
26403
- inManual = true;
26404
- continue;
26405
- }
26406
- if (trimmed === "# end manual") {
26407
- if (msgid)
26408
- flush();
26409
- inManual = false;
26410
- continue;
26411
- }
26412
- if (trimmed === "" || trimmed.startsWith("#,")) {
26413
- if (msgid)
26414
- flush();
26415
- continue;
26416
- }
26417
- if (trimmed.startsWith("#:")) {
26418
- if (msgid)
26419
- flush();
26420
- locations.push(trimmed.slice(3).trim());
26421
- continue;
26422
- }
26423
- if (trimmed.startsWith("#.")) {
26424
- const hashMatch = trimmed.match(/hash:(\w+)/);
26425
- if (hashMatch)
26426
- hash = hashMatch[1];
26427
- continue;
26428
- }
26429
- if (trimmed.startsWith("#~")) {
26430
- const rest = trimmed.slice(3).trim();
26431
- if (rest.startsWith("msgid")) {
26432
- if (msgid)
26433
- flush();
26434
- obsolete = true;
26435
- msgid = extractQuoted(rest.slice(5));
26436
- } else if (rest.startsWith("msgstr")) {
26437
- msgstr = extractQuoted(rest.slice(6));
26438
- }
26439
- continue;
26440
- }
26441
- if (trimmed.startsWith("msgid")) {
26442
- if (msgid)
26443
- flush();
26444
- msgid = extractQuoted(trimmed.slice(5));
26445
- } else if (trimmed.startsWith("msgstr")) {
26446
- msgstr = extractQuoted(trimmed.slice(6));
26447
- }
26448
- }
26449
- flush();
26450
- return entries;
26451
- }
26452
- function writePo(entries) {
26453
- const lines = [];
26454
- const manual = entries.filter((e) => e.manual);
26455
- const active = entries.filter((e) => !e.obsolete && !e.manual);
26456
- const obsolete = entries.filter((e) => e.obsolete);
26457
- if (manual.length > 0) {
26458
- lines.push("# manual");
26459
- for (const entry of manual) {
26460
- lines.push(`msgid ${quoteString(entry.msgid)}`);
26461
- lines.push(`msgstr ${quoteString(entry.msgstr)}`);
26462
- lines.push("");
26463
- }
26464
- lines.push("# end manual");
26465
- lines.push("");
26466
- }
26467
- for (const entry of active) {
26468
- for (const loc of entry.locations) {
26469
- lines.push(`#: ${loc}`);
26470
- }
26471
- lines.push(`#. hash:${entry.hash}`);
26472
- lines.push(`msgid ${quoteString(entry.msgid)}`);
26473
- lines.push(`msgstr ${quoteString(entry.msgstr)}`);
26474
- lines.push("");
26475
- }
26476
- if (obsolete.length > 0) {
26477
- lines.push("# Obsolete entries");
26478
- lines.push("");
26479
- for (const entry of obsolete) {
26480
- lines.push(`#~ msgid ${quoteString(entry.msgid)}`);
26481
- lines.push(`#~ msgstr ${quoteString(entry.msgstr)}`);
26482
- lines.push("");
26483
- }
26484
- }
26485
- return lines.join(`
26486
- `);
26487
- }
26488
- function mergeCatalog(existing, extracted) {
26489
- const existingMap = new Map;
26490
- for (const entry of existing) {
26491
- existingMap.set(entry.msgid, entry);
26492
- }
26493
- const manualEntries = existing.filter((e) => e.manual);
26494
- const manualIds = new Set(manualEntries.map((e) => e.msgid));
26495
- const result = [];
26496
- const seen = new Set;
26497
- for (const [msgid, locations] of extracted) {
26498
- seen.add(msgid);
26499
- if (manualIds.has(msgid))
26500
- continue;
26501
- const prev = existingMap.get(msgid);
26502
- result.push({
26503
- msgid,
26504
- msgstr: prev?.msgstr ?? "",
26505
- locations: [...locations].sort(),
26506
- hash: hashMsgid(msgid),
26507
- obsolete: false
26508
- });
26509
- }
26510
- for (const entry of existing) {
26511
- if (!seen.has(entry.msgid) && !entry.obsolete && !entry.manual && entry.msgstr) {
26512
- result.push({
26513
- ...entry,
26514
- obsolete: true,
26515
- locations: []
26516
- });
26517
- }
26518
- }
26519
- result.sort((a, b) => a.msgid.localeCompare(b.msgid));
26520
- return [...manualEntries, ...result];
26521
- }
26522
- function extractQuoted(s) {
26523
- const trimmed = s.trim();
26524
- if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
26525
- return trimmed.slice(1, -1).replace(/\\n/g, `
26526
- `).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
26527
- }
26528
- return trimmed;
26529
- }
26530
- function quoteString(s) {
26531
- const escaped = s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
26532
- return `"${escaped}"`;
26533
- }
26534
-
26535
- // src/i18n/compile.ts
26536
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
26537
- import { join as join4 } from "path";
26538
- function compileCatalog(poPath) {
26539
- const content = readFileSync3(poPath, "utf-8");
26540
- const entries = parsePo(content);
26541
- const result = {};
26542
- for (const entry of entries) {
26543
- if (!entry.obsolete && entry.msgstr) {
26544
- result[entry.msgid] = entry.msgstr;
26545
- }
26546
- }
26547
- const sorted = {};
26548
- for (const key of Object.keys(result).sort()) {
26549
- sorted[key] = result[key];
26550
- }
26551
- return sorted;
26552
- }
26553
- function compileAllCatalogs(localesDir, outDir) {
26554
- mkdirSync3(outDir, { recursive: true });
26555
- for (const file of readdirSync2(localesDir)) {
26556
- if (!file.endsWith(".po"))
26557
- continue;
26558
- const locale = file.replace(".po", "");
26559
- const compiled = compileCatalog(join4(localesDir, file));
26560
- writeFileSync3(join4(outDir, `${locale}.json`), JSON.stringify(compiled));
26561
- }
26562
- }
26563
-
26564
- // src/i18n/plugin.ts
26565
- function i18nExtractPlugin(collector, rootDir) {
26566
- return {
26567
- name: "arc-i18n-extract",
26568
- setup(build2) {
26569
- build2.onLoad({ filter: /\.tsx?$/ }, async (args) => {
26570
- const source = await Bun.file(args.path).text();
26571
- const relPath = args.path.startsWith(rootDir) ? args.path.slice(rootDir.length + 1) : args.path;
26572
- const transRegex = /<Trans>\s*([^<{]+?)\s*<\/Trans>/g;
26573
- for (const match2 of source.matchAll(transRegex)) {
26574
- const msgid = match2[1].trim();
26575
- if (!msgid)
26576
- continue;
26577
- const line = source.substring(0, match2.index).split(`
26578
- `).length;
26579
- const loc = `${relPath}:${line}`;
26580
- if (!collector.has(msgid))
26581
- collector.set(msgid, new Set);
26582
- collector.get(msgid).add(loc);
26583
- }
26584
- const tRegex = /\bt`([^`]+)`/g;
26585
- for (const match2 of source.matchAll(tRegex)) {
26586
- const msgid = match2[1];
26587
- if (!msgid)
26588
- continue;
26589
- const line = source.substring(0, match2.index).split(`
26590
- `).length;
26591
- const loc = `${relPath}:${line}`;
26592
- if (!collector.has(msgid))
26593
- collector.set(msgid, new Set);
26594
- collector.get(msgid).add(loc);
26595
- }
26596
- return;
26597
- });
26598
- }
26599
- };
26600
- }
26601
-
26602
- // src/i18n/index.ts
26603
- function readTranslationsConfig(rootDir) {
26604
- const pkgPath = join5(rootDir, "package.json");
26605
- if (!existsSync4(pkgPath))
26606
- return null;
26607
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
26608
- const config = pkg.arc?.translations;
26609
- if (!config?.locales?.length)
26610
- return null;
26611
- return {
26612
- locales: config.locales,
26613
- sourceLocale: config.sourceLocale ?? config.locales[0]
26614
- };
26615
- }
26616
- async function finalizeTranslations(rootDir, outDir, collector) {
26617
- const config = readTranslationsConfig(rootDir);
26618
- if (!config || collector.size === 0)
26619
- return;
26620
- const localesJsonDir = join5(outDir, "locales");
26621
- mkdirSync4(localesJsonDir, { recursive: true });
26622
- console.log(` Extracted ${collector.size} translatable string(s) for ${config.locales.length} locale(s)`);
26623
- for (const locale of config.locales) {
26624
- const poPath = join5(rootDir, "locales", `${locale}.po`);
26625
- mkdirSync4(dirname4(poPath), { recursive: true });
26626
- const existing = existsSync4(poPath) ? parsePo(readFileSync4(poPath, "utf-8")) : [];
26627
- const merged = mergeCatalog(existing, collector);
26628
- writeFileSync4(poPath, writePo(merged));
26629
- const compiled = compileCatalog(poPath);
26630
- writeFileSync4(join5(localesJsonDir, `${locale}.json`), JSON.stringify(compiled));
26594
+ rebuildsInProgress.set(key, rebuildPromise);
26595
+ await rebuildPromise;
26596
+ }
26597
+ });
26598
+ devContexts.push({
26599
+ configPath,
26600
+ config,
26601
+ configDir,
26602
+ contexts,
26603
+ watchers: [watcher]
26604
+ });
26605
+ console.log("");
26606
+ log(`Watching for changes in ${configDir}...`, "cyan");
26607
+ }
26608
+ console.log("");
26609
+ log("Development mode started. Press Ctrl+C to stop.", "green");
26610
+ process.on("SIGINT", () => {
26611
+ console.log("");
26612
+ log("Shutting down dev mode...", "yellow");
26613
+ for (const devCtx of devContexts) {
26614
+ for (const watcher of devCtx.watchers) {
26615
+ watcher.close();
26616
+ }
26617
+ }
26618
+ process.exit(0);
26619
+ });
26620
+ } catch (err) {
26621
+ console.error("Error in dev command:", err);
26622
+ process.exit(1);
26631
26623
  }
26632
26624
  }
26633
26625
 
26626
+ // src/platform/shared.ts
26627
+ import { copyFileSync, existsSync as existsSync10, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
26628
+ import { dirname as dirname6, join as join11 } from "path";
26629
+
26630
+ // src/builder/module-builder.ts
26631
+ import { execSync } from "child_process";
26632
+ import {
26633
+ existsSync as existsSync7,
26634
+ mkdirSync as mkdirSync6,
26635
+ readFileSync as readFileSync7,
26636
+ readdirSync as readdirSync4,
26637
+ rmSync,
26638
+ writeFileSync as writeFileSync6
26639
+ } from "fs";
26640
+ import { basename as basename2, dirname as dirname5, join as join8, relative as relative3 } from "path";
26641
+ init_i18n();
26642
+ init_compile();
26643
+
26634
26644
  // src/builder/build-cache.ts
26635
26645
  import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
26636
26646
  import { join as join6 } from "path";
@@ -26799,6 +26809,24 @@ async function pAll(tasks, concurrency = DEFAULT_CONCURRENCY) {
26799
26809
  }
26800
26810
 
26801
26811
  // src/builder/module-builder.ts
26812
+ function singleReactPlugin(rootDir) {
26813
+ const reactPkgs = ["react", "react-dom", "react-dom/client", "react/jsx-runtime"];
26814
+ return {
26815
+ name: "single-react",
26816
+ setup(build2) {
26817
+ const resolved = new Map;
26818
+ for (const spec of reactPkgs) {
26819
+ try {
26820
+ resolved.set(spec, Bun.resolveSync(spec, rootDir));
26821
+ } catch {}
26822
+ }
26823
+ build2.onResolve({ filter: /^(react|react-dom|react-dom\/client|react\/jsx-runtime)$/ }, (args) => {
26824
+ const path4 = resolved.get(args.path);
26825
+ return path4 ? { path: path4 } : null;
26826
+ });
26827
+ }
26828
+ };
26829
+ }
26802
26830
  function jsxDevShimPlugin() {
26803
26831
  return {
26804
26832
  name: "jsx-dev-runtime-shim",
@@ -26914,8 +26942,7 @@ async function buildContextClient(pkg, rootDir, client, cache, noCache) {
26914
26942
  console.log(` building: ${pkg.name} (${client.name})`);
26915
26943
  const peerDeps = Object.keys(pkg.packageJson.peerDependencies ?? {});
26916
26944
  const allDeps = pkg.packageJson.dependencies ?? {};
26917
- const npmDeps = Object.entries(allDeps).filter(([, spec]) => !spec.startsWith("workspace:")).map(([name]) => name);
26918
- const externals = [...peerDeps, ...npmDeps];
26945
+ const externals = [...peerDeps, ...Object.keys(allDeps)];
26919
26946
  const result = await Bun.build({
26920
26947
  entrypoints: [pkg.entrypoint],
26921
26948
  outdir: join8(outDir, "main"),
@@ -26956,125 +26983,163 @@ async function buildContextPackages(rootDir, packages, cache, noCache) {
26956
26983
  }
26957
26984
  return { declarationErrors };
26958
26985
  }
26959
- async function buildModulesByChunks(rootDir, outDir, plan, cache, noCache) {
26986
+ async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollector) {
26960
26987
  mkdirSync6(outDir, { recursive: true });
26961
- const i18nCollector = new Map;
26962
- const aggregateModules = [];
26963
- let allCached = true;
26964
- for (const chunk of plan.chunks) {
26965
- const members = plan.groups.get(chunk) ?? [];
26966
- if (members.length === 0)
26967
- continue;
26968
- const chunkOutDir = join8(outDir, chunk);
26969
- mkdirSync6(chunkOutDir, { recursive: true });
26970
- const result = await buildChunkGroup(rootDir, chunkOutDir, chunk, members, cache, noCache, i18nCollector);
26971
- aggregateModules.push(...result.modules);
26972
- if (!result.cached)
26973
- allCached = false;
26974
- }
26975
- await finalizeTranslations(rootDir, join8(outDir, ".."), i18nCollector);
26976
- return { modules: aggregateModules, cached: allCached };
26977
- }
26978
- async function buildChunkGroup(rootDir, chunkOutDir, chunk, members, cache, noCache, i18nCollector) {
26979
- const unitId = `modules-chunk:${chunk}`;
26980
- const pkgHashes = members.map((m) => ({
26981
- name: m.pkg.name,
26982
- safeName: m.safeName,
26983
- moduleName: m.moduleName,
26984
- srcHash: pkgSourceHash(m.pkg)
26985
- }));
26988
+ const publicMembers = plan.groups.get("public") ?? [];
26989
+ const protectedGroups = plan.chunks.filter((c) => c !== "public").map((c) => ({ name: c, members: plan.groups.get(c) ?? [] })).filter((g) => g.members.length > 0);
26990
+ const unitId = "browser-app";
26991
+ const allMembers = [];
26992
+ for (const m of publicMembers) {
26993
+ allMembers.push({ name: m.pkg.name, group: "public", srcHash: pkgSourceHash(m.pkg) });
26994
+ }
26995
+ for (const g of protectedGroups) {
26996
+ for (const m of g.members) {
26997
+ allMembers.push({ name: m.pkg.name, group: g.name, srcHash: pkgSourceHash(m.pkg) });
26998
+ }
26999
+ }
26986
27000
  const inputHash = sha256OfJson({
26987
- chunk,
26988
- pkgHashes,
26989
- externals: SHELL_EXTERNALS,
27001
+ members: allMembers,
27002
+ groups: [
27003
+ "initial",
27004
+ ...protectedGroups.map((g) => g.name).sort()
27005
+ ],
26990
27006
  define: { ONLY_SERVER: "false", ONLY_BROWSER: "true", ONLY_CLIENT: "true" }
26991
27007
  });
26992
27008
  if (!noCache && isCacheHit(cache, unitId, inputHash)) {
26993
- const existing = cache.units[unitId]?.outputHashes ?? {};
26994
- const modules2 = [];
26995
- let missing = false;
26996
- for (const h of pkgHashes) {
26997
- const file = `${h.safeName}.js`;
26998
- const filePath = join8(chunkOutDir, file);
26999
- if (!existsSync7(filePath)) {
27000
- missing = true;
27001
- break;
27002
- }
27003
- modules2.push({
27004
- file,
27005
- name: h.moduleName,
27006
- chunk,
27007
- hash: existing[h.safeName] ?? sha256Hex(readFileSync7(filePath))
27008
- });
27009
+ const cached = cache.units[unitId]?.outputHashes;
27010
+ if (cached?._manifest) {
27011
+ try {
27012
+ const m = JSON.parse(cached._manifest);
27013
+ const allFiles = [
27014
+ m.initial.file,
27015
+ ...Object.values(m.groups).map((g) => g.file),
27016
+ ...m.sharedChunks
27017
+ ];
27018
+ if (allFiles.every((f) => existsSync7(join8(outDir, f)))) {
27019
+ console.log(` \u2713 cached: ${unitId}`);
27020
+ return { ...m, cached: true };
27021
+ }
27022
+ } catch {}
27009
27023
  }
27010
- if (!missing) {
27011
- console.log(` \u2713 cached: ${unitId} (${modules2.length} module(s))`);
27012
- return { modules: modules2, cached: true };
27024
+ }
27025
+ console.log(` building: ${unitId} (initial: ${publicMembers.length} modules, groups: ${protectedGroups.map((g) => `${g.name}=${g.members.length}`).join(",") || "none"})`);
27026
+ if (existsSync7(outDir)) {
27027
+ for (const f of readdirSync4(outDir)) {
27028
+ if (f.endsWith(".js"))
27029
+ rmSync(join8(outDir, f), { force: true });
27013
27030
  }
27014
- console.log(` rebuilding ${unitId}: output file missing`);
27015
27031
  }
27016
- console.log(` building: ${unitId} (${members.length} module(s))`);
27017
- const tmpDir = join8(chunkOutDir, "_entries");
27032
+ const tmpDir = join8(outDir, "_entries");
27018
27033
  mkdirSync6(tmpDir, { recursive: true });
27019
- const entrypoints = [];
27020
- const fileToModuleName = new Map;
27021
- for (const m of members) {
27022
- fileToModuleName.set(m.safeName, m.moduleName);
27023
- const wrapperFile = join8(tmpDir, `${m.safeName}.ts`);
27024
- writeFileSync6(wrapperFile, `export * from "${m.pkg.name}";
27034
+ const importLines = (pkgs) => pkgs.map((m) => `import "${m.pkg.name}";`).join(`
27035
+ `);
27036
+ const initialEntry = join8(tmpDir, "initial.ts");
27037
+ writeFileSync6(initialEntry, `${importLines(publicMembers)}
27038
+ export { startApp } from "@arcote.tech/platform";
27039
+ `);
27040
+ const entryPaths = [initialEntry];
27041
+ const groupModuleMap = new Map;
27042
+ for (const g of protectedGroups) {
27043
+ const entry = join8(tmpDir, `${g.name}.ts`);
27044
+ writeFileSync6(entry, `${importLines(g.members)}
27045
+ `);
27046
+ entryPaths.push(entry);
27047
+ groupModuleMap.set(g.name, g.members.map((m) => m.moduleName));
27048
+ }
27049
+ const allMemberPkgs = new Map;
27050
+ for (const m of publicMembers)
27051
+ allMemberPkgs.set(m.pkg.name, m.pkg);
27052
+ for (const g of protectedGroups)
27053
+ for (const m of g.members)
27054
+ allMemberPkgs.set(m.pkg.name, m.pkg);
27055
+ const patchedPkgJsons = [];
27056
+ for (const pkg of allMemberPkgs.values()) {
27057
+ const pkgJsonPath = join8(pkg.path, "package.json");
27058
+ if (!existsSync7(pkgJsonPath))
27059
+ continue;
27060
+ const original = readFileSync7(pkgJsonPath, "utf-8");
27061
+ const parsed = JSON.parse(original);
27062
+ if (parsed.sideEffects === true)
27063
+ continue;
27064
+ parsed.sideEffects = true;
27065
+ writeFileSync6(pkgJsonPath, JSON.stringify(parsed, null, 2) + `
27025
27066
  `);
27026
- entrypoints.push(wrapperFile);
27067
+ patchedPkgJsons.push({ path: pkgJsonPath, original });
27027
27068
  }
27028
- const arcExternalPlugin = {
27029
- name: "arc-external",
27030
- setup(build2) {
27031
- build2.onResolve({ filter: /^@arcote\.tech\// }, (args) => {
27032
- return { path: args.path, external: true };
27033
- });
27034
- }
27035
- };
27036
- const result = await Bun.build({
27037
- entrypoints,
27038
- outdir: chunkOutDir,
27039
- splitting: true,
27040
- format: "esm",
27041
- target: "browser",
27042
- external: [...SHELL_EXTERNALS],
27043
- plugins: [
27044
- arcExternalPlugin,
27045
- jsxDevShimPlugin(),
27046
- i18nExtractPlugin(i18nCollector, rootDir)
27047
- ],
27048
- naming: "[name].[ext]",
27049
- define: {
27050
- ONLY_SERVER: "false",
27051
- ONLY_BROWSER: "true",
27052
- ONLY_CLIENT: "true"
27053
- }
27054
- });
27069
+ let result;
27070
+ try {
27071
+ result = await Bun.build({
27072
+ entrypoints: entryPaths,
27073
+ outdir: outDir,
27074
+ splitting: true,
27075
+ format: "esm",
27076
+ target: "browser",
27077
+ external: [],
27078
+ plugins: [
27079
+ singleReactPlugin(rootDir),
27080
+ jsxDevShimPlugin(),
27081
+ i18nExtractPlugin(i18nCollector, rootDir)
27082
+ ],
27083
+ naming: "[name].[ext]",
27084
+ define: {
27085
+ ONLY_SERVER: "false",
27086
+ ONLY_BROWSER: "true",
27087
+ ONLY_CLIENT: "true",
27088
+ "process.env.NODE_ENV": '"production"'
27089
+ }
27090
+ });
27091
+ } finally {
27092
+ for (const p of patchedPkgJsons)
27093
+ writeFileSync6(p.path, p.original);
27094
+ }
27095
+ rmSync(tmpDir, { recursive: true, force: true });
27055
27096
  if (!result.success) {
27056
- console.error(`Chunk "${chunk}" build failed:`);
27057
27097
  for (const log2 of result.logs)
27058
27098
  console.error(log2);
27059
- throw new Error(`Module chunk build failed: ${chunk}`);
27099
+ throw new Error("Browser app build failed");
27100
+ }
27101
+ let initialFile = "";
27102
+ let initialHash = "";
27103
+ const groups = {};
27104
+ const sharedChunks = [];
27105
+ for (const out of result.outputs) {
27106
+ const name = basename2(out.path);
27107
+ if (out.kind === "entry-point") {
27108
+ const bytes = readFileSync7(out.path);
27109
+ const hash = sha256Hex(bytes).slice(0, 16);
27110
+ const stem = name.replace(/\.js$/, "");
27111
+ const finalName = `${stem}.${hash}.js`;
27112
+ const finalPath = join8(outDir, finalName);
27113
+ rmSync(finalPath, { force: true });
27114
+ writeFileSync6(finalPath, bytes);
27115
+ rmSync(out.path, { force: true });
27116
+ if (stem === "initial") {
27117
+ initialFile = finalName;
27118
+ initialHash = hash;
27119
+ } else {
27120
+ groups[stem] = {
27121
+ file: finalName,
27122
+ hash,
27123
+ modules: groupModuleMap.get(stem) ?? []
27124
+ };
27125
+ }
27126
+ } else if (out.kind === "chunk") {
27127
+ sharedChunks.push(name);
27128
+ }
27060
27129
  }
27061
- rmSync(tmpDir, { recursive: true, force: true });
27062
- const outputHashes = {};
27063
- const modules = result.outputs.filter((o) => o.kind === "entry-point").map((o) => {
27064
- const file = basename2(o.path);
27065
- const safeName = file.replace(/\.js$/, "");
27066
- const bytes = readFileSync7(o.path);
27067
- const hash = sha256Hex(bytes);
27068
- outputHashes[safeName] = hash;
27069
- return {
27070
- file,
27071
- name: fileToModuleName.get(safeName) ?? safeName,
27072
- chunk,
27073
- hash
27074
- };
27130
+ if (!initialFile) {
27131
+ throw new Error("Browser app build: initial entry not found in outputs");
27132
+ }
27133
+ const manifest = {
27134
+ initial: { file: initialFile, hash: initialHash },
27135
+ groups,
27136
+ sharedChunks,
27137
+ cached: false
27138
+ };
27139
+ updateCache(cache, unitId, inputHash, {
27140
+ outputHashes: { _manifest: JSON.stringify(manifest) }
27075
27141
  });
27076
- updateCache(cache, unitId, inputHash, { outputHashes });
27077
- return { modules, cached: false };
27142
+ return manifest;
27078
27143
  }
27079
27144
  async function buildTranslations(rootDir, arcDir, cache, noCache) {
27080
27145
  const localesDir = join8(rootDir, "locales");
@@ -27216,17 +27281,10 @@ import {
27216
27281
  existsSync as existsSync8,
27217
27282
  mkdirSync as mkdirSync7,
27218
27283
  readFileSync as readFileSync8,
27219
- realpathSync as realpathSync2,
27220
27284
  unlinkSync as unlinkSync2,
27221
27285
  writeFileSync as writeFileSync7
27222
27286
  } from "fs";
27223
- import { dirname as dirname6, join as join9 } from "path";
27224
- import { fileURLToPath as fileURLToPath5 } from "url";
27225
- function locatePlatformServerEntry() {
27226
- const here = fileURLToPath5(import.meta.url);
27227
- const arcRoot = realpathSync2(dirname6(dirname6(dirname6(dirname6(here)))));
27228
- return join9(arcRoot, "packages", "platform", "src", "index.server.ts");
27229
- }
27287
+ import { join as join9 } from "path";
27230
27288
  async function extractAccessMap(rootDir, packages) {
27231
27289
  const serverBundles = packages.filter((p) => isContextPackage(p.packageJson)).map((p) => ({
27232
27290
  name: p.name,
@@ -27244,8 +27302,7 @@ async function extractAccessMap(rootDir, packages) {
27244
27302
  env: {
27245
27303
  ...process.env,
27246
27304
  ARC_ACCESS_BUNDLES: JSON.stringify(serverBundles),
27247
- ARC_ACCESS_OUT: outPath,
27248
- ARC_PLATFORM_ENTRY: locatePlatformServerEntry()
27305
+ ARC_ACCESS_OUT: outPath
27249
27306
  },
27250
27307
  stdout: "pipe",
27251
27308
  stderr: "inherit"
@@ -27276,9 +27333,11 @@ if (!out) {
27276
27333
  process.exit(2);
27277
27334
  }
27278
27335
 
27279
- // Direct file-path import \u2014 bypasses node_modules resolution entirely.
27280
- // Avoids quirks with bun-link snapshots holding stale package.json exports.
27281
- const platform = await import(process.env.ARC_PLATFORM_ENTRY);
27336
+ // Bare-specifier import \u2014 Bun walks up from this worker's location
27337
+ // (<rootDir>/.arc/.tmp/) to <rootDir>/node_modules and finds the package.
27338
+ // Single entry (./src/index.ts) \u2014 React imports on top level are benign
27339
+ // (createContext, function defs); no DOM access until actual render.
27340
+ const platform = await import("@arcote.tech/platform");
27282
27341
 
27283
27342
  for (const { name, path } of bundles) {
27284
27343
  try {
@@ -27436,7 +27495,7 @@ function resolveWorkspace() {
27436
27495
  err("No package.json found");
27437
27496
  process.exit(1);
27438
27497
  }
27439
- const rootDir = dirname7(packageJsonPath);
27498
+ const rootDir = dirname6(packageJsonPath);
27440
27499
  const rootPkg = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
27441
27500
  const appName = rootPkg.name ?? "Arc App";
27442
27501
  const arcDir = join11(rootDir, ".arc", "platform");
@@ -27471,8 +27530,7 @@ function resolveWorkspace() {
27471
27530
  rootPkg,
27472
27531
  appName,
27473
27532
  arcDir,
27474
- modulesDir: join11(arcDir, "modules"),
27475
- shellDir: join11(arcDir, "shell"),
27533
+ browserDir: join11(arcDir, "browser"),
27476
27534
  assetsDir: join11(arcDir, "assets"),
27477
27535
  publicDir: join11(rootDir, "public"),
27478
27536
  packages,
@@ -27492,32 +27550,27 @@ async function buildAll(ws, opts = {}) {
27492
27550
  `);
27493
27551
  const plan = planChunks(ws.packages, accessMap);
27494
27552
  ok(`Chunks: ${plan.chunks.map((c) => `${c}(${plan.groups.get(c)?.length ?? 0})`).join(", ")}`);
27495
- const [modulesResult] = await Promise.all([
27496
- buildModulesByChunks(ws.rootDir, ws.modulesDir, plan, cache, noCache),
27497
- buildShell(ws, cache, noCache),
27553
+ const i18nCollector = new Map;
27554
+ const [browserResult] = await Promise.all([
27555
+ buildBrowserApp(ws.rootDir, ws.browserDir, plan, cache, noCache, i18nCollector),
27498
27556
  buildStyles(ws.rootDir, ws.arcDir, ws.packages, themePath, cache, noCache),
27499
27557
  copyBrowserAssets(ws, cache, noCache),
27500
27558
  buildTranslations(ws.rootDir, ws.arcDir, cache, noCache)
27501
27559
  ]);
27560
+ const { finalizeTranslations: finalizeTranslations3 } = await Promise.resolve().then(() => (init_i18n(), exports_i18n));
27561
+ await finalizeTranslations3(ws.rootDir, ws.arcDir, i18nCollector);
27502
27562
  collectFrameworkDeps(ws.arcDir, ws.rootDir, ws.packages);
27503
27563
  saveBuildCache(ws.arcDir, cache);
27504
- const finalManifest = assembleManifest(ws, modulesResult.modules, plan.chunks, cache);
27505
- writeFileSync9(join11(ws.modulesDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
27564
+ const finalManifest = assembleManifest(ws, browserResult, cache);
27565
+ writeFileSync9(join11(ws.arcDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
27506
27566
  return finalManifest;
27507
27567
  }
27508
- function assembleManifest(ws, modules, chunks, cache) {
27509
- const shellEntries = {};
27510
- for (const [unitId, entry] of Object.entries(cache.units)) {
27511
- if (unitId.startsWith("shell:") && entry.outputHash) {
27512
- shellEntries[unitId] = entry.outputHash;
27513
- }
27514
- }
27515
- const shellHash = sha256OfJson(shellEntries);
27568
+ function assembleManifest(ws, browser, cache) {
27516
27569
  const stylesHash = cache.units["styles"]?.outputHash ?? "";
27517
27570
  return {
27518
- modules,
27519
- chunks,
27520
- shellHash,
27571
+ initial: browser.initial,
27572
+ groups: browser.groups,
27573
+ sharedChunks: browser.sharedChunks,
27521
27574
  stylesHash,
27522
27575
  buildTime: new Date().toISOString()
27523
27576
  };
@@ -27619,170 +27672,12 @@ async function copyBrowserAssets(ws, cache, noCache) {
27619
27672
  const outputHashes = {};
27620
27673
  for (const asset of assets) {
27621
27674
  const dest = join11(ws.assetsDir, asset.to);
27622
- mkdirSync9(dirname7(dest), { recursive: true });
27675
+ mkdirSync9(dirname6(dest), { recursive: true });
27623
27676
  copyFileSync(asset.src, dest);
27624
27677
  outputHashes[asset.to] = sha256Hex(readFileSync10(dest));
27625
27678
  }
27626
27679
  updateCache(cache, unitId, inputHash, { outputHashes });
27627
27680
  }
27628
- function collectArcPeerDeps(packages) {
27629
- const seen = new Set;
27630
- for (const pkg of ["@arcote.tech/arc", "@arcote.tech/arc-ds", "@arcote.tech/arc-react", "@arcote.tech/platform"]) {
27631
- seen.add(pkg);
27632
- }
27633
- for (const wp of packages) {
27634
- const peerDeps = wp.packageJson.peerDependencies ?? {};
27635
- for (const dep of Object.keys(peerDeps)) {
27636
- if (dep.startsWith("@arcote.tech/"))
27637
- seen.add(dep);
27638
- }
27639
- }
27640
- return [...seen].map((pkg) => {
27641
- const short = pkg === "@arcote.tech/platform" ? "platform" : pkg.replace("@arcote.tech/", "");
27642
- return [short, pkg];
27643
- });
27644
- }
27645
- var REACT_ENTRIES = [
27646
- [
27647
- "react",
27648
- `import React from "react";
27649
- export default React;
27650
- export const {
27651
- Children, Component, Fragment, Profiler, PureComponent, StrictMode, Suspense,
27652
- cloneElement, createContext, createElement, createRef, forwardRef, isValidElement,
27653
- lazy, memo, startTransition, use, useCallback, useContext, useDebugValue,
27654
- useDeferredValue, useEffect, useId, useImperativeHandle, useInsertionEffect,
27655
- useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore,
27656
- useTransition, version, useActionState, useOptimistic,
27657
- } = React;`
27658
- ],
27659
- ["jsx-runtime", `export { jsx, jsxs, Fragment } from "react/jsx-runtime";`],
27660
- [
27661
- "jsx-dev-runtime",
27662
- `export { jsxDEV, Fragment } from "react/jsx-dev-runtime";`
27663
- ],
27664
- [
27665
- "react-dom",
27666
- `import ReactDOM from "react-dom";
27667
- export default ReactDOM;
27668
- export const { createPortal, flushSync } = ReactDOM;`
27669
- ],
27670
- [
27671
- "react-dom-client",
27672
- `export { createRoot, hydrateRoot } from "react-dom/client";`
27673
- ]
27674
- ];
27675
- var REACT_OUTPUT_FILES = REACT_ENTRIES.map(([n]) => `${n}.js`);
27676
- var SHELL_BASE_EXTERNAL = [
27677
- "react",
27678
- "react-dom",
27679
- "react/jsx-runtime",
27680
- "react/jsx-dev-runtime",
27681
- "react-dom/client"
27682
- ];
27683
- var sourceFilter2 = (rel) => {
27684
- if (rel.startsWith("dist/") || rel.startsWith("dist"))
27685
- return false;
27686
- if (rel.includes("/node_modules/") || rel.startsWith("node_modules"))
27687
- return false;
27688
- if (rel.startsWith(".arc/") || rel.startsWith(".arc"))
27689
- return false;
27690
- return true;
27691
- };
27692
- function arcPkgSrcHash(rootDir, pkg) {
27693
- const srcDir = join11(rootDir, "node_modules", pkg, "src");
27694
- if (existsSync10(srcDir))
27695
- return sha256OfDir(srcDir, sourceFilter2);
27696
- return sha256OfDir(join11(rootDir, "node_modules", pkg), sourceFilter2);
27697
- }
27698
- async function buildShellReact(shellDir, tmpDir, rootDir, cache, noCache) {
27699
- const unitId = "shell:react";
27700
- const inputHash = sha256OfJson({
27701
- react: readInstalledVersion(rootDir, "react"),
27702
- "react-dom": readInstalledVersion(rootDir, "react-dom"),
27703
- entries: REACT_ENTRIES.map(([k, v]) => [k, v])
27704
- });
27705
- const requiredOutputs = REACT_OUTPUT_FILES.map((f) => join11(shellDir, f));
27706
- if (!noCache && isCacheHit(cache, unitId, inputHash, requiredOutputs)) {
27707
- console.log(` \u2713 cached: shell:react`);
27708
- return;
27709
- }
27710
- console.log(` building: shell:react`);
27711
- const reactEps = [];
27712
- for (const [name, code] of REACT_ENTRIES) {
27713
- const f = join11(tmpDir, `${name}.ts`);
27714
- await Bun.write(f, code);
27715
- reactEps.push(f);
27716
- }
27717
- const r = await Bun.build({
27718
- entrypoints: reactEps,
27719
- outdir: shellDir,
27720
- splitting: true,
27721
- format: "esm",
27722
- target: "browser",
27723
- naming: "[name].[ext]"
27724
- });
27725
- if (!r.success) {
27726
- for (const l of r.logs)
27727
- console.error(l);
27728
- throw new Error("Shell React build failed");
27729
- }
27730
- const outputHash = sha256OfFiles(requiredOutputs);
27731
- updateCache(cache, unitId, inputHash, { outputHash });
27732
- }
27733
- async function buildShellArcEntry(shortName, pkg, allArcPkgs, shellDir, tmpDir, rootDir, cache, noCache) {
27734
- const unitId = `shell:arc:${shortName}`;
27735
- const otherExternals = allArcPkgs.filter((p) => p !== pkg);
27736
- const inputHash = sha256OfJson({
27737
- pkg,
27738
- version: readInstalledVersion(rootDir, pkg),
27739
- src: arcPkgSrcHash(rootDir, pkg),
27740
- base: SHELL_BASE_EXTERNAL,
27741
- others: [...otherExternals].sort()
27742
- });
27743
- const outputFile = join11(shellDir, `${shortName}.js`);
27744
- if (!noCache && isCacheHit(cache, unitId, inputHash, [outputFile])) {
27745
- console.log(` \u2713 cached: ${unitId}`);
27746
- return;
27747
- }
27748
- console.log(` building: ${unitId}`);
27749
- const f = join11(tmpDir, `${shortName}.ts`);
27750
- await Bun.write(f, `export * from "${pkg}";
27751
- `);
27752
- const r = await Bun.build({
27753
- entrypoints: [f],
27754
- outdir: shellDir,
27755
- format: "esm",
27756
- target: "browser",
27757
- naming: "[name].[ext]",
27758
- external: [...SHELL_BASE_EXTERNAL, ...otherExternals],
27759
- define: {
27760
- ONLY_SERVER: "false",
27761
- ONLY_BROWSER: "true",
27762
- ONLY_CLIENT: "true"
27763
- }
27764
- });
27765
- if (!r.success) {
27766
- for (const l of r.logs)
27767
- console.error(l);
27768
- throw new Error(`Shell build failed for ${pkg}`);
27769
- }
27770
- const outputHash = sha256OfFiles([outputFile]);
27771
- updateCache(cache, unitId, inputHash, { outputHash });
27772
- }
27773
- async function buildShell(ws, cache, noCache) {
27774
- mkdirSync9(ws.shellDir, { recursive: true });
27775
- const tmpDir = join11(ws.shellDir, "_tmp");
27776
- mkdirSync9(tmpDir, { recursive: true });
27777
- const arcEntries = collectArcPeerDeps(ws.packages);
27778
- const allArcPkgs = arcEntries.map(([, pkg]) => pkg);
27779
- const tasks = [
27780
- () => buildShellReact(ws.shellDir, tmpDir, ws.rootDir, cache, noCache),
27781
- ...arcEntries.map(([short, pkg]) => () => buildShellArcEntry(short, pkg, allArcPkgs, ws.shellDir, tmpDir, ws.rootDir, cache, noCache))
27782
- ];
27783
- await pAll(tasks);
27784
- rmSync2(tmpDir, { recursive: true, force: true });
27785
- }
27786
27681
  async function loadServerContext(ws) {
27787
27682
  globalThis.ONLY_SERVER = true;
27788
27683
  globalThis.ONLY_BROWSER = false;
@@ -27830,24 +27725,25 @@ async function loadServerContext(ws) {
27830
27725
  async function platformBuild(opts = {}) {
27831
27726
  const ws = resolveWorkspace();
27832
27727
  const manifest = await buildAll(ws, { noCache: opts.noCache });
27833
- ok(`Platform built \u2014 ${manifest.modules.length} module(s)`);
27728
+ const groupCount = Object.keys(manifest.groups).length;
27729
+ ok(`Platform built \u2014 initial + ${groupCount} group(s)`);
27834
27730
  }
27835
27731
 
27836
27732
  // src/commands/platform-deploy.ts
27837
- import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
27838
- import { dirname as dirname9, join as join17 } from "path";
27839
- import { fileURLToPath as fileURLToPath7 } from "url";
27733
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
27734
+ import { dirname as dirname8, join as join19 } from "path";
27735
+ import { fileURLToPath as fileURLToPath6 } from "url";
27840
27736
 
27841
27737
  // src/deploy/bootstrap.ts
27842
27738
  var {spawn: spawn4 } = globalThis.Bun;
27843
27739
  import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync13 } from "fs";
27844
27740
  import { tmpdir as tmpdir2 } from "os";
27845
- import { join as join15 } from "path";
27741
+ import { join as join17 } from "path";
27846
27742
 
27847
27743
  // src/deploy/ansible.ts
27848
27744
  import { spawn as nodeSpawn } from "child_process";
27849
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
27850
- import { tmpdir } from "os";
27745
+ import { existsSync as existsSync11, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
27746
+ import { homedir, tmpdir } from "os";
27851
27747
  import { join as join12 } from "path";
27852
27748
 
27853
27749
  // src/deploy/assets.ts
@@ -28042,7 +27938,22 @@ var ANSIBLE_SITE_YML = `---
28042
27938
  - { policy: deny, dir: incoming }
28043
27939
  - { policy: allow, dir: outgoing }
28044
27940
 
28045
- - name: Open firewall ports
27941
+ - name: Remove legacy ufw limit rule on SSH (replaced by plain allow)
27942
+ # If a prior bootstrap installed \`ufw limit 22/tcp\`, drop it \u2014 otherwise
27943
+ # the limit rule shadows the allow rule and rate-throttles deploy flows.
27944
+ ufw:
27945
+ rule: limit
27946
+ port: "{{ ssh_port }}"
27947
+ proto: tcp
27948
+ delete: true
27949
+ ignore_errors: true
27950
+
27951
+ - name: Open firewall ports (SSH key-only auth, no brute-force surface)
27952
+ # SSH on port 22: PasswordAuthentication=no + key-only means brute force
27953
+ # is impossible without the operator's private key. Rate-limiting (ufw
27954
+ # limit / fail2ban sshd jail) breaks legitimate deploy flows that open
27955
+ # many short SSH connections in sequence (canSsh -> sshExec -> scp -> ...).
27956
+ # 80/443: Caddy ACME + app traffic, never rate-limited.
28046
27957
  ufw:
28047
27958
  rule: allow
28048
27959
  port: "{{ item }}"
@@ -28056,17 +27967,18 @@ var ANSIBLE_SITE_YML = `---
28056
27967
  ufw:
28057
27968
  state: enabled
28058
27969
 
28059
- - name: Configure fail2ban for sshd
27970
+ - name: Disable fail2ban sshd jail
27971
+ # Key-only SSH + ufw rate-limit make fail2ban for sshd redundant and
27972
+ # actively harmful when the operator's IP roams. Keep fail2ban installed
27973
+ # for future jails (web/db) but turn off the sshd jail explicitly.
28060
27974
  copy:
28061
27975
  dest: /etc/fail2ban/jail.local
28062
27976
  content: |
28063
27977
  [sshd]
28064
- enabled = true
28065
- port = {{ ssh_port }}
28066
- maxretry = 5
28067
- findtime = 600
28068
- bantime = 3600
27978
+ enabled = false
27979
+ {% if extra_allowed_ips %}
28069
27980
  ignoreip = 127.0.0.1/8 ::1 {{ extra_allowed_ips | join(' ') }}
27981
+ {% endif %}
28070
27982
  mode: "0644"
28071
27983
  notify: restart fail2ban
28072
27984
 
@@ -28114,18 +28026,32 @@ async function materializeAssets(targetDir, files) {
28114
28026
  }
28115
28027
 
28116
28028
  // src/deploy/ansible.ts
28029
+ function pickSshKeyForAnsible(configured) {
28030
+ if (configured) {
28031
+ const expanded = configured.startsWith("~") ? join12(homedir(), configured.slice(1)) : configured;
28032
+ return existsSync11(expanded) ? expanded : null;
28033
+ }
28034
+ for (const name of ["id_ed25519", "id_ecdsa", "id_rsa"]) {
28035
+ const path4 = join12(homedir(), ".ssh", name);
28036
+ if (existsSync11(path4))
28037
+ return path4;
28038
+ }
28039
+ return null;
28040
+ }
28117
28041
  async function runAnsible(inputs) {
28118
28042
  const workDir = join12(tmpdir(), "arc-deploy", `ansible-${Date.now()}`);
28119
28043
  mkdirSync10(workDir, { recursive: true });
28120
28044
  await materializeAssets(workDir, ASSETS.ansible);
28121
28045
  const user = inputs.asRoot ? "root" : inputs.target.user;
28122
28046
  const port = inputs.ansible?.sshPort ?? inputs.target.port;
28047
+ const sshKey = pickSshKeyForAnsible(inputs.target.sshKey);
28048
+ const sshKeyArg = sshKey ? ` -o IdentitiesOnly=yes -i ${sshKey}` : "";
28123
28049
  const inventory = [
28124
28050
  "[arc]",
28125
28051
  `${inputs.target.host} ansible_user=${user} ansible_port=${port}`,
28126
28052
  "",
28127
28053
  "[arc:vars]",
28128
- "ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new -o BatchMode=yes'",
28054
+ `ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new -o BatchMode=yes -o PreferredAuthentications=publickey${sshKeyArg}'`,
28129
28055
  "ansible_python_interpreter=/usr/bin/python3",
28130
28056
  ""
28131
28057
  ].join(`
@@ -28224,7 +28150,7 @@ function generateCompose({ cfg }) {
28224
28150
  for (const [name, env2] of Object.entries(cfg.envs)) {
28225
28151
  const upperName = name.toUpperCase().replace(/-/g, "_");
28226
28152
  lines.push(` arc-${name}:`);
28227
- lines.push(` image: \${ARC_IMAGE_${upperName}:?Run \\\`arc platform deploy ${name}\\\` to publish an image first}`);
28153
+ lines.push(` image: \${ARC_IMAGE_${upperName}:-arc-${name}:not-deployed}`);
28228
28154
  lines.push(` container_name: arc-${name}`);
28229
28155
  lines.push(" restart: unless-stopped");
28230
28156
  lines.push(" volumes:");
@@ -28278,16 +28204,16 @@ async function generateHtpasswd(user, password) {
28278
28204
  // src/deploy/terraform.ts
28279
28205
  import { spawn as nodeSpawn2 } from "child_process";
28280
28206
  import { createHash as createHash2 } from "crypto";
28281
- import { existsSync as existsSync11, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
28282
- import { homedir } from "os";
28207
+ import { existsSync as existsSync12, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
28208
+ import { homedir as homedir2 } from "os";
28283
28209
  import { join as join13 } from "path";
28284
28210
  async function runTerraform(inputs) {
28285
28211
  const wsHash = createHash2("sha256").update(inputs.workspaceDir).digest("hex").slice(0, 16);
28286
- const workDir = join13(homedir(), ".arc-deploy", wsHash, "tf");
28212
+ const workDir = join13(homedir2(), ".arc-deploy", wsHash, "tf");
28287
28213
  mkdirSync11(workDir, { recursive: true });
28288
28214
  await materializeAssets(workDir, ASSETS.terraform);
28289
28215
  const sshPubKey = inputs.tf.sshPublicKey ?? expandHome("~/.ssh/id_ed25519.pub");
28290
- if (!existsSync11(expandHome(sshPubKey))) {
28216
+ if (!existsSync12(expandHome(sshPubKey))) {
28291
28217
  throw new Error(`SSH public key not found at ${sshPubKey}. Set provision.terraform.sshPublicKey in deploy.arc.json.`);
28292
28218
  }
28293
28219
  const tfvars = [
@@ -28358,33 +28284,93 @@ function expandHome(p) {
28358
28284
  }
28359
28285
 
28360
28286
  // src/deploy/config.ts
28361
- import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync12 } from "fs";
28287
+ import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
28288
+ import { join as join15 } from "path";
28289
+
28290
+ // src/deploy/env-file.ts
28291
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
28362
28292
  import { join as join14 } from "path";
28293
+ function loadDeployEnvFiles(rootDir, envNames) {
28294
+ const globalsPath = join14(rootDir, "deploy.arc.env");
28295
+ const globals = existsSync13(globalsPath) ? parseEnvFile(readFileSync11(globalsPath, "utf-8"), globalsPath) : {};
28296
+ const perEnv = {};
28297
+ for (const name of envNames) {
28298
+ const envPath = join14(rootDir, `deploy.arc.${name}.env`);
28299
+ if (existsSync13(envPath)) {
28300
+ perEnv[name] = parseEnvFile(readFileSync11(envPath, "utf-8"), envPath);
28301
+ }
28302
+ }
28303
+ return { globals, perEnv };
28304
+ }
28305
+ function applyDeployGlobals(globals) {
28306
+ for (const [k, v] of Object.entries(globals)) {
28307
+ if (process.env[k] === undefined) {
28308
+ process.env[k] = v;
28309
+ }
28310
+ }
28311
+ }
28312
+ function parseEnvFile(content, pathForErrors) {
28313
+ const out = {};
28314
+ const lines = content.split(/\r?\n/);
28315
+ for (let i = 0;i < lines.length; i++) {
28316
+ const raw = lines[i];
28317
+ const line = raw.trim();
28318
+ if (!line || line.startsWith("#"))
28319
+ continue;
28320
+ const eq = line.indexOf("=");
28321
+ if (eq <= 0) {
28322
+ throw new Error(`${pathForErrors}:${i + 1}: malformed line (expected KEY=VALUE): ${raw}`);
28323
+ }
28324
+ const key = line.slice(0, eq).trim();
28325
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
28326
+ throw new Error(`${pathForErrors}:${i + 1}: invalid variable name "${key}"`);
28327
+ }
28328
+ let value = line.slice(eq + 1).trim();
28329
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
28330
+ value = value.slice(1, -1);
28331
+ }
28332
+ out[key] = value;
28333
+ }
28334
+ return out;
28335
+ }
28336
+
28337
+ // src/deploy/config.ts
28363
28338
  var DEPLOY_CONFIG_FILE = "deploy.arc.json";
28364
28339
  function deployConfigPath(rootDir) {
28365
- return join14(rootDir, DEPLOY_CONFIG_FILE);
28340
+ return join15(rootDir, DEPLOY_CONFIG_FILE);
28366
28341
  }
28367
28342
  function deployConfigExists(rootDir) {
28368
- return existsSync12(deployConfigPath(rootDir));
28343
+ return existsSync14(deployConfigPath(rootDir));
28369
28344
  }
28370
28345
  function loadDeployConfig(rootDir) {
28371
28346
  const path4 = deployConfigPath(rootDir);
28372
- if (!existsSync12(path4)) {
28347
+ if (!existsSync14(path4)) {
28373
28348
  throw new Error(`Missing ${DEPLOY_CONFIG_FILE} at ${path4}`);
28374
28349
  }
28375
- const raw = readFileSync11(path4, "utf-8");
28350
+ const raw = readFileSync12(path4, "utf-8");
28376
28351
  let parsed;
28377
28352
  try {
28378
28353
  parsed = JSON.parse(raw);
28379
28354
  } catch (e) {
28380
28355
  throw new Error(`Invalid JSON in ${DEPLOY_CONFIG_FILE}: ${e.message}`);
28381
28356
  }
28357
+ const envNames = isObject(parsed) && isObject(parsed.envs) ? Object.keys(parsed.envs) : [];
28358
+ const envFiles = loadDeployEnvFiles(rootDir, envNames);
28359
+ applyDeployGlobals(envFiles.globals);
28382
28360
  const expanded = expandEnvVars(parsed, process.env);
28383
- return validateDeployConfig(expanded);
28361
+ const validated = validateDeployConfig(expanded);
28362
+ for (const [envName, vars] of Object.entries(envFiles.perEnv)) {
28363
+ if (!(envName in validated.envs))
28364
+ continue;
28365
+ const env2 = validated.envs[envName];
28366
+ const merged = { ...vars, ...env2.envVars ?? {} };
28367
+ validated.envs[envName] = { ...env2, envVars: merged };
28368
+ }
28369
+ return validated;
28384
28370
  }
28385
28371
  function saveDeployConfig(rootDir, cfg) {
28386
28372
  const path4 = deployConfigPath(rootDir);
28387
- const raw = existsSync12(path4) ? JSON.parse(readFileSync11(path4, "utf-8")) : {};
28373
+ const raw = existsSync14(path4) ? JSON.parse(readFileSync12(path4, "utf-8")) : {};
28388
28374
  raw.target = { ...raw.target, ...cfg.target };
28389
28375
  writeFileSync12(path4, JSON.stringify(raw, null, 2) + `
28390
28376
  `);
@@ -28552,22 +28538,47 @@ function cfgErr(path4, expected) {
28552
28538
 
28553
28539
  // src/deploy/ssh.ts
28554
28540
  var {spawn: spawn3 } = globalThis.Bun;
28541
+ import { existsSync as existsSync15 } from "fs";
28542
+ import { homedir as homedir3 } from "os";
28543
+ import { join as join16 } from "path";
28544
+ function pickSshKey(target) {
28545
+ if (target.sshKey) {
28546
+ const expanded = target.sshKey.startsWith("~") ? join16(homedir3(), target.sshKey.slice(1)) : target.sshKey;
28547
+ return existsSync15(expanded) ? expanded : null;
28548
+ }
28549
+ for (const name of ["id_ed25519", "id_ecdsa", "id_rsa"]) {
28550
+ const path4 = join16(homedir3(), ".ssh", name);
28551
+ if (existsSync15(path4))
28552
+ return path4;
28553
+ }
28554
+ return null;
28555
+ }
28555
28556
  async function streamToString(stream2) {
28556
28557
  if (!stream2 || typeof stream2 === "number")
28557
28558
  return "";
28558
28559
  return new Response(stream2).text();
28559
28560
  }
28560
28561
  function baseSshArgs(target) {
28562
+ const key = pickSshKey(target);
28561
28563
  const args = [
28562
28564
  "-o",
28563
28565
  "BatchMode=yes",
28564
28566
  "-o",
28565
28567
  "StrictHostKeyChecking=accept-new",
28568
+ "-o",
28569
+ "PreferredAuthentications=publickey",
28570
+ "-o",
28571
+ "ConnectTimeout=5",
28572
+ "-o",
28573
+ "ServerAliveInterval=10",
28574
+ "-o",
28575
+ "ServerAliveCountMax=2",
28566
28576
  "-p",
28567
28577
  String(target.port)
28568
28578
  ];
28569
- if (target.sshKey)
28570
- args.push("-i", target.sshKey);
28579
+ if (key) {
28580
+ args.push("-o", "IdentitiesOnly=yes", "-i", key);
28581
+ }
28571
28582
  return args;
28572
28583
  }
28573
28584
  async function sshExec(target, cmd, opts = {}) {
@@ -28610,28 +28621,23 @@ async function canSsh(target) {
28610
28621
  const res = await sshExec(target, "true", { quiet: true });
28611
28622
  return res.exitCode === 0;
28612
28623
  }
28613
- async function waitForSsh(target, opts = {}) {
28614
- const timeout = opts.timeoutMs ?? 300000;
28615
- const interval = opts.intervalMs ?? 1e4;
28616
- const start = Date.now();
28617
- while (Date.now() - start < timeout) {
28618
- if (await canSsh(target))
28619
- return;
28620
- await Bun.sleep(interval);
28621
- }
28622
- throw new Error(`Timed out waiting for SSH on ${target.user}@${target.host}`);
28623
- }
28624
28624
  async function scpUpload(target, localPath, remotePath) {
28625
+ const key = pickSshKey(target);
28625
28626
  const args = [
28626
28627
  "-o",
28627
28628
  "BatchMode=yes",
28628
28629
  "-o",
28629
28630
  "StrictHostKeyChecking=accept-new",
28631
+ "-o",
28632
+ "PreferredAuthentications=publickey",
28633
+ "-o",
28634
+ "ConnectTimeout=5",
28630
28635
  "-P",
28631
28636
  String(target.port)
28632
28637
  ];
28633
- if (target.sshKey)
28634
- args.push("-i", target.sshKey);
28638
+ if (key) {
28639
+ args.push("-o", "IdentitiesOnly=yes", "-i", key);
28640
+ }
28635
28641
  args.push(localPath, `${target.user}@${target.host}:${remotePath}`);
28636
28642
  const proc2 = spawn3({ cmd: ["scp", ...args], stderr: "pipe" });
28637
28643
  const [stderr, exitCode] = await Promise.all([
@@ -28689,6 +28695,18 @@ JSON`);
28689
28695
  }
28690
28696
 
28691
28697
  // src/deploy/bootstrap.ts
28698
+ async function waitForAnySsh(targets, opts = {}) {
28699
+ const timeout = opts.timeoutMs ?? 300000;
28700
+ const interval = opts.intervalMs ?? 5000;
28701
+ const start = Date.now();
28702
+ while (Date.now() - start < timeout) {
28703
+ const results = await Promise.all(targets.map((t) => canSsh(t)));
28704
+ if (results.some(Boolean))
28705
+ return;
28706
+ await Bun.sleep(interval);
28707
+ }
28708
+ throw new Error(`Timed out waiting for SSH on ${targets.map((t) => `${t.user}@${t.host}`).join(" or ")}`);
28709
+ }
28692
28710
  async function bootstrap(inputs) {
28693
28711
  const { cfg, state, rootDir } = inputs;
28694
28712
  if (state.kind === "unreachable") {
@@ -28710,12 +28728,16 @@ async function bootstrap(inputs) {
28710
28728
  cfg.target.host = tfOut.serverIp;
28711
28729
  saveDeployConfig(rootDir, cfg);
28712
28730
  log2("Waiting for SSH to come up...");
28713
- await waitForSsh({ ...cfg.target, user: "root" });
28731
+ await waitForAnySsh([
28732
+ { ...cfg.target, user: "root" },
28733
+ { ...cfg.target, user: cfg.target.user }
28734
+ ]);
28714
28735
  ok("SSH reachable");
28715
28736
  }
28716
- if (state.kind === "unreachable" || state.kind === "no-docker") {
28737
+ const needAnsible = state.kind === "unreachable" || state.kind === "no-docker" || inputs.forceAnsible === true;
28738
+ if (needAnsible) {
28717
28739
  log2("Running Ansible bootstrap (Docker + firewall + SSH hardening)...");
28718
- const deployUserWorks = state.kind === "no-docker" && await canSsh(cfg.target);
28740
+ const deployUserWorks = state.kind !== "unreachable" && await canSsh(cfg.target);
28719
28741
  const asRoot = !deployUserWorks;
28720
28742
  await runAnsible({
28721
28743
  target: cfg.target,
@@ -28724,7 +28746,8 @@ async function bootstrap(inputs) {
28724
28746
  });
28725
28747
  ok("Host bootstrapped");
28726
28748
  }
28727
- if (state.kind !== "ready") {
28749
+ const needUpStack = state.kind !== "ready" || state.marker === null || state.marker.configHash !== inputs.configHash || !await isRegistryRunning(cfg);
28750
+ if (needUpStack) {
28728
28751
  await upStack(inputs);
28729
28752
  ok("Docker stack up");
28730
28753
  }
@@ -28734,9 +28757,14 @@ async function bootstrap(inputs) {
28734
28757
  updatedAt: new Date().toISOString()
28735
28758
  });
28736
28759
  }
28760
+ async function isRegistryRunning(cfg) {
28761
+ const res = await sshExec(cfg.target, `cd ${cfg.target.remoteDir} && docker compose ps --status running --format '{{.Service}}' 2>/dev/null || true`, { quiet: true });
28762
+ return res.stdout.split(`
28763
+ `).map((s) => s.trim()).includes("registry");
28764
+ }
28737
28765
  async function upStack(inputs) {
28738
28766
  const { cfg } = inputs;
28739
- const workDir = join15(tmpdir2(), "arc-deploy", `stack-${Date.now()}`);
28767
+ const workDir = join17(tmpdir2(), "arc-deploy", `stack-${Date.now()}`);
28740
28768
  mkdirSync12(workDir, { recursive: true });
28741
28769
  await assertRegistryDnsResolves(cfg);
28742
28770
  const password = process.env[cfg.registry.passwordEnv];
@@ -28744,17 +28772,17 @@ async function upStack(inputs) {
28744
28772
  throw new Error(`Registry password env var ${cfg.registry.passwordEnv} is not set. ` + `Set it (e.g. \`export ${cfg.registry.passwordEnv}=...\`) before bootstrap.`);
28745
28773
  }
28746
28774
  const htpasswdLine = await generateHtpasswd(cfg.registry.username, password);
28747
- writeFileSync13(join15(workDir, "htpasswd"), htpasswdLine);
28748
- writeFileSync13(join15(workDir, "Caddyfile"), generateCaddyfile(cfg));
28749
- writeFileSync13(join15(workDir, "docker-compose.yml"), generateCompose({ cfg }));
28775
+ writeFileSync13(join17(workDir, "htpasswd"), htpasswdLine);
28776
+ writeFileSync13(join17(workDir, "Caddyfile"), generateCaddyfile(cfg));
28777
+ writeFileSync13(join17(workDir, "docker-compose.yml"), generateCompose({ cfg }));
28750
28778
  await assertExec(cfg.target, `sudo mkdir -p ${cfg.target.remoteDir} && sudo chown ${cfg.target.user}:${cfg.target.user} ${cfg.target.remoteDir}`);
28751
28779
  for (const name of Object.keys(cfg.envs)) {
28752
28780
  await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/${name}`);
28753
28781
  }
28754
28782
  await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/registry-auth`);
28755
- await scpUpload(cfg.target, join15(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
28756
- await scpUpload(cfg.target, join15(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
28757
- await scpUpload(cfg.target, join15(workDir, "htpasswd"), `${cfg.target.remoteDir}/registry-auth/htpasswd`);
28783
+ await scpUpload(cfg.target, join17(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
28784
+ await scpUpload(cfg.target, join17(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
28785
+ await scpUpload(cfg.target, join17(workDir, "htpasswd"), `${cfg.target.remoteDir}/registry-auth/htpasswd`);
28758
28786
  await assertExec(cfg.target, `touch ${cfg.target.remoteDir}/.env`);
28759
28787
  await assertExec(cfg.target, `cd ${cfg.target.remoteDir} && docker compose pull --ignore-pull-failures caddy registry && docker compose up -d caddy registry`);
28760
28788
  await sshDockerLogin(cfg);
@@ -28768,18 +28796,23 @@ async function sshDockerLogin(cfg) {
28768
28796
  if (!password) {
28769
28797
  throw new Error(`Registry password env var ${cfg.registry.passwordEnv} is not set on the deploy host (CLI machine).`);
28770
28798
  }
28771
- const cmd = `echo "$ARC_REGISTRY_PASSWORD_FORWARDED" | docker login ${cfg.registry.domain} -u ${cfg.registry.username} --password-stdin`;
28799
+ const cmd = `docker login ${cfg.registry.domain} -u ${cfg.registry.username} --password-stdin`;
28772
28800
  const proc2 = spawn4({
28773
28801
  cmd: [
28774
28802
  "ssh",
28775
28803
  ...baseSshArgs(cfg.target),
28776
28804
  `${cfg.target.user}@${cfg.target.host}`,
28777
28805
  "--",
28778
- `ARC_REGISTRY_PASSWORD_FORWARDED='${password.replace(/'/g, "'\\''")}' bash -c ${JSON.stringify(cmd)}`
28806
+ cmd
28779
28807
  ],
28808
+ stdin: "pipe",
28780
28809
  stdout: "pipe",
28781
28810
  stderr: "pipe"
28782
28811
  });
28812
+ if (proc2.stdin) {
28813
+ await proc2.stdin.write(new TextEncoder().encode(password));
28814
+ await proc2.stdin.end?.();
28815
+ }
28783
28816
  const exit = await proc2.exited;
28784
28817
  if (exit !== 0) {
28785
28818
  const stderr = await new Response(proc2.stderr).text();
@@ -28801,24 +28834,39 @@ async function listConfiguredEnvs(cfg) {
28801
28834
  return [...set];
28802
28835
  }
28803
28836
  async function assertRegistryDnsResolves(cfg) {
28837
+ const apex = apexDomain(cfg.registry.domain);
28838
+ let nameservers = await digQuery("8.8.8.8", "NS", apex);
28839
+ nameservers = nameservers.map((s) => s.replace(/\.$/, ""));
28840
+ const sources = [...nameservers, "1.1.1.1", "8.8.8.8"];
28841
+ let lastAnswers = [];
28842
+ for (const source of sources) {
28843
+ const answers = await digQuery(source, "A", cfg.registry.domain);
28844
+ if (answers.length === 0)
28845
+ continue;
28846
+ lastAnswers = answers;
28847
+ if (answers.includes(cfg.target.host))
28848
+ return;
28849
+ }
28850
+ if (lastAnswers.length === 0) {
28851
+ throw new Error(`Registry DNS not configured: ${cfg.registry.domain} doesn't resolve. ` + `Add an A record pointing to ${cfg.target.host} and re-run deploy.`);
28852
+ }
28853
+ throw new Error(`Registry DNS mismatch: ${cfg.registry.domain} resolves to [${lastAnswers.join(", ")}], ` + `but target host is ${cfg.target.host}. Update the A record before continuing.`);
28854
+ }
28855
+ function apexDomain(host) {
28856
+ const parts = host.split(".");
28857
+ return parts.slice(-2).join(".");
28858
+ }
28859
+ async function digQuery(server, type, name) {
28804
28860
  const proc2 = spawn4({
28805
- cmd: ["dig", "+short", "+time=3", "+tries=1", cfg.registry.domain],
28861
+ cmd: ["dig", `@${server}`, "+short", "+time=3", "+tries=1", type, name],
28806
28862
  stdout: "pipe",
28807
28863
  stderr: "ignore"
28808
28864
  });
28809
28865
  const exit = await proc2.exited;
28810
- if (exit !== 0) {
28811
- err(`\`dig\` is not available \u2014 skipping DNS pre-flight for ${cfg.registry.domain}.`);
28812
- return;
28813
- }
28814
- const resolved = (await new Response(proc2.stdout).text()).split(`
28866
+ if (exit !== 0)
28867
+ return [];
28868
+ return (await new Response(proc2.stdout).text()).split(`
28815
28869
  `).map((s) => s.trim()).filter(Boolean);
28816
- if (resolved.length === 0) {
28817
- throw new Error(`Registry DNS not configured: ${cfg.registry.domain} doesn't resolve. ` + `Add an A record pointing to ${cfg.target.host} and re-run deploy.`);
28818
- }
28819
- if (!resolved.includes(cfg.target.host)) {
28820
- throw new Error(`Registry DNS mismatch: ${cfg.registry.domain} resolves to [${resolved.join(", ")}], ` + `but target host is ${cfg.target.host}. Update the A record before continuing.`);
28821
- }
28822
28870
  }
28823
28871
 
28824
28872
  // src/deploy/deploy-env.ts
@@ -28831,7 +28879,7 @@ async function updateEnvDeployment(opts) {
28831
28879
  const envPath = `${cfg.target.remoteDir}/.env`;
28832
28880
  const escapedRef = fullRef.replace(/"/g, "\\\"");
28833
28881
  const updateScript = [
28834
- `touch ${envPath}`,
28882
+ `touch ${envPath} && `,
28835
28883
  `awk -v line="${envVarName}=${escapedRef}" -v key="${envVarName}=" '`,
28836
28884
  ` BEGIN { replaced=0 } `,
28837
28885
  ` $0 ~ "^"key { print line; replaced=1; next } `,
@@ -28881,15 +28929,15 @@ var {spawn: spawn5 } = globalThis.Bun;
28881
28929
  import { createHash as createHash3 } from "crypto";
28882
28930
  import {
28883
28931
  copyFileSync as copyFileSync2,
28884
- existsSync as existsSync13,
28932
+ existsSync as existsSync16,
28885
28933
  mkdirSync as mkdirSync13,
28886
- readFileSync as readFileSync12,
28887
- realpathSync as realpathSync3,
28934
+ readFileSync as readFileSync13,
28935
+ realpathSync as realpathSync2,
28888
28936
  writeFileSync as writeFileSync14
28889
28937
  } from "fs";
28890
28938
  import { tmpdir as tmpdir3 } from "os";
28891
- import { dirname as dirname8, join as join16 } from "path";
28892
- import { fileURLToPath as fileURLToPath6 } from "url";
28939
+ import { dirname as dirname7, join as join18 } from "path";
28940
+ import { fileURLToPath as fileURLToPath5 } from "url";
28893
28941
 
28894
28942
  // src/deploy/image-template.ts
28895
28943
  function generateDockerfile(inputs) {
@@ -28938,8 +28986,8 @@ function generateDockerfile(inputs) {
28938
28986
  // src/deploy/image.ts
28939
28987
  async function buildImage(ws, opts) {
28940
28988
  await ensureDocker();
28941
- const manifestPath = join16(ws.modulesDir, "manifest.json");
28942
- if (!existsSync13(manifestPath)) {
28989
+ const manifestPath = join18(ws.arcDir, "manifest.json");
28990
+ if (!existsSync16(manifestPath)) {
28943
28991
  throw new Error(`No build manifest at ${manifestPath}. Run \`arc platform build\` first or omit --skip-build.`);
28944
28992
  }
28945
28993
  embedCliBundle(ws);
@@ -28949,12 +28997,13 @@ async function buildImage(ws, opts) {
28949
28997
  const dockerfileInputs = collectDockerfileInputs(ws);
28950
28998
  const dockerfile = generateDockerfile(dockerfileInputs);
28951
28999
  const buildContextDir = ws.rootDir;
28952
- const dockerfileDir = join16(tmpdir3(), `arc-image-${Date.now()}`);
29000
+ const dockerfileDir = join18(tmpdir3(), `arc-image-${Date.now()}`);
28953
29001
  mkdirSync13(dockerfileDir, { recursive: true });
28954
- const dockerfilePath = join16(dockerfileDir, "Dockerfile");
29002
+ const dockerfilePath = join18(dockerfileDir, "Dockerfile");
28955
29003
  writeFileSync14(dockerfilePath, dockerfile);
28956
29004
  const buildArgs = [
28957
29005
  "build",
29006
+ "--platform=linux/amd64",
28958
29007
  "-f",
28959
29008
  dockerfilePath,
28960
29009
  "-t",
@@ -28976,20 +29025,20 @@ async function buildImage(ws, opts) {
28976
29025
  }
28977
29026
  function embedCliBundle(ws) {
28978
29027
  const source = locateCliBundle();
28979
- const target = join16(ws.arcDir, "host.js");
29028
+ const target = join18(ws.arcDir, "host.js");
28980
29029
  copyFileSync2(source, target);
28981
29030
  }
28982
29031
  function locateCliBundle() {
28983
- const here = fileURLToPath6(import.meta.url);
28984
- let cur = dirname8(here);
29032
+ const here = fileURLToPath5(import.meta.url);
29033
+ let cur = dirname7(here);
28985
29034
  while (cur !== "/" && cur !== "") {
28986
- const candidate = join16(cur, "package.json");
28987
- if (existsSync13(candidate)) {
29035
+ const candidate = join18(cur, "package.json");
29036
+ if (existsSync16(candidate)) {
28988
29037
  try {
28989
- const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
29038
+ const pkg = JSON.parse(readFileSync13(candidate, "utf-8"));
28990
29039
  if (pkg.name === "@arcote.tech/arc-cli") {
28991
- const distIndex = join16(realpathSync3(cur), "dist", "index.js");
28992
- if (!existsSync13(distIndex)) {
29040
+ const distIndex = join18(realpathSync2(cur), "dist", "index.js");
29041
+ if (!existsSync16(distIndex)) {
28993
29042
  throw new Error(`arc-cli bundle missing at ${distIndex}. Run \`bun run build\` in packages/cli/.`);
28994
29043
  }
28995
29044
  return distIndex;
@@ -28999,7 +29048,7 @@ function locateCliBundle() {
28999
29048
  throw e;
29000
29049
  }
29001
29050
  }
29002
- const parent = dirname8(cur);
29051
+ const parent = dirname7(cur);
29003
29052
  if (parent === cur)
29004
29053
  break;
29005
29054
  cur = parent;
@@ -29021,17 +29070,17 @@ async function ensureDocker() {
29021
29070
  }
29022
29071
  }
29023
29072
  function computeContentHash(manifestPath) {
29024
- const raw = JSON.parse(readFileSync12(manifestPath, "utf-8"));
29073
+ const raw = JSON.parse(readFileSync13(manifestPath, "utf-8"));
29025
29074
  delete raw.buildTime;
29026
29075
  const canonical = JSON.stringify(raw);
29027
29076
  return createHash3("sha256").update(canonical).digest("hex").slice(0, 12);
29028
29077
  }
29029
29078
  function collectDockerfileInputs(ws) {
29030
- const hasPublicDir = existsSync13(join16(ws.rootDir, "public"));
29031
- const hasLocales = existsSync13(join16(ws.rootDir, "locales"));
29079
+ const hasPublicDir = existsSync16(join18(ws.rootDir, "public"));
29080
+ const hasLocales = existsSync16(join18(ws.rootDir, "locales"));
29032
29081
  let manifestPath;
29033
29082
  for (const name of ["manifest.webmanifest", "manifest.json"]) {
29034
- if (existsSync13(join16(ws.rootDir, name))) {
29083
+ if (existsSync16(join18(ws.rootDir, name))) {
29035
29084
  manifestPath = name;
29036
29085
  break;
29037
29086
  }
@@ -29852,14 +29901,14 @@ async function platformDeploy(envArg, options = {}) {
29852
29901
  err(`Unknown env "${envArg}". Known: ${Object.keys(cfg.envs).join(", ")}`);
29853
29902
  process.exit(1);
29854
29903
  })() : Object.keys(cfg.envs);
29855
- const manifestPath = join17(ws.modulesDir, "manifest.json");
29904
+ const manifestPath = join19(ws.arcDir, "manifest.json");
29856
29905
  if (!options.imageTag) {
29857
- const needBuild = options.rebuild || !existsSync14(manifestPath);
29906
+ const needBuild = options.rebuild || !existsSync17(manifestPath);
29858
29907
  if (needBuild && !options.skipBuild) {
29859
29908
  log2("Building platform...");
29860
29909
  await buildAll(ws, { noCache: options.rebuild });
29861
29910
  ok("Build complete");
29862
- } else if (!existsSync14(manifestPath)) {
29911
+ } else if (!existsSync17(manifestPath)) {
29863
29912
  err("No build found and --skip-build was set.");
29864
29913
  process.exit(1);
29865
29914
  }
@@ -29884,25 +29933,26 @@ async function platformDeploy(envArg, options = {}) {
29884
29933
  log2(`contentHash: ${contentHash}`);
29885
29934
  return;
29886
29935
  }
29887
- log2(`Logging in to ${cfg.registry.domain}...`);
29888
- await dockerLogin(cfg.registry);
29889
- log2(`Pushing ${fullRef}...`);
29890
- await dockerPush(fullRef);
29891
- ok("Image pushed");
29892
29936
  }
29893
29937
  log2("Inspecting remote server...");
29894
29938
  const state = await detectRemoteState(cfg);
29895
29939
  log2(`Remote state: ${state.kind}`);
29896
29940
  const cliVersion = readCliVersion();
29897
29941
  const configHash = await hashDeployConfig(ws.rootDir);
29898
- if (state.kind !== "ready") {
29899
- await bootstrap({
29900
- cfg,
29901
- rootDir: ws.rootDir,
29902
- state,
29903
- cliVersion,
29904
- configHash
29905
- });
29942
+ await bootstrap({
29943
+ cfg,
29944
+ rootDir: ws.rootDir,
29945
+ state,
29946
+ cliVersion,
29947
+ configHash,
29948
+ forceAnsible: options.forceBootstrap
29949
+ });
29950
+ if (!options.imageTag) {
29951
+ log2(`Logging in to ${cfg.registry.domain}...`);
29952
+ await dockerLogin(cfg.registry);
29953
+ log2(`Pushing ${fullRef}...`);
29954
+ await dockerPush(fullRef);
29955
+ ok("Image pushed");
29906
29956
  }
29907
29957
  for (const env2 of targetEnvs) {
29908
29958
  log2(`Updating env "${env2}"...`);
@@ -29921,17 +29971,17 @@ async function platformDeploy(envArg, options = {}) {
29921
29971
  }
29922
29972
  function readCliVersion() {
29923
29973
  try {
29924
- let cur = dirname9(fileURLToPath7(import.meta.url));
29925
- const root = dirname9(cur).startsWith("/") ? "/" : ".";
29974
+ let cur = dirname8(fileURLToPath6(import.meta.url));
29975
+ const root = dirname8(cur).startsWith("/") ? "/" : ".";
29926
29976
  while (cur !== root && cur !== "") {
29927
- const candidate = join17(cur, "package.json");
29928
- if (existsSync14(candidate)) {
29929
- const pkg = JSON.parse(readFileSync13(candidate, "utf-8"));
29977
+ const candidate = join19(cur, "package.json");
29978
+ if (existsSync17(candidate)) {
29979
+ const pkg = JSON.parse(readFileSync14(candidate, "utf-8"));
29930
29980
  if (pkg.name === "@arcote.tech/arc-cli") {
29931
29981
  return pkg.version ?? "unknown";
29932
29982
  }
29933
29983
  }
29934
- const parent = dirname9(cur);
29984
+ const parent = dirname8(cur);
29935
29985
  if (parent === cur)
29936
29986
  break;
29937
29987
  cur = parent;
@@ -29942,16 +29992,16 @@ function readCliVersion() {
29942
29992
  }
29943
29993
  }
29944
29994
  async function hashDeployConfig(rootDir) {
29945
- const p2 = join17(rootDir, "deploy.arc.json");
29946
- const content = readFileSync13(p2);
29995
+ const p2 = join19(rootDir, "deploy.arc.json");
29996
+ const content = readFileSync14(p2);
29947
29997
  const hasher = new Bun.CryptoHasher("sha256");
29948
29998
  hasher.update(content);
29949
29999
  return hasher.digest("hex").slice(0, 16);
29950
30000
  }
29951
30001
 
29952
30002
  // src/platform/startup.ts
29953
- import { existsSync as existsSync16, readFileSync as readFileSync14, watch } from "fs";
29954
- import { join as join19 } from "path";
30003
+ import { existsSync as existsSync19, readFileSync as readFileSync15, watch } from "fs";
30004
+ import { join as join21 } from "path";
29955
30005
 
29956
30006
  // ../host/src/create-server.ts
29957
30007
  var import_jsonwebtoken = __toESM(require_jsonwebtoken(), 1);
@@ -31197,8 +31247,8 @@ function querySubscriptionHandler() {
31197
31247
  subscriptionId,
31198
31248
  data: data ?? null
31199
31249
  });
31200
- } catch (err2) {
31201
- console.error(`[Arc] Query subscription error:`, err2);
31250
+ } catch (err3) {
31251
+ console.error(`[Arc] Query subscription error:`, err3);
31202
31252
  }
31203
31253
  };
31204
31254
  sendData();
@@ -31401,25 +31451,14 @@ async function createArcServer(config) {
31401
31451
  };
31402
31452
  }
31403
31453
  // src/platform/server.ts
31404
- import { existsSync as existsSync15, mkdirSync as mkdirSync14 } from "fs";
31405
- import { join as join18 } from "path";
31406
- function generateShellHtml(appName, manifest, arcEntries) {
31407
- const arcImports = {};
31408
- if (arcEntries) {
31409
- for (const [short, pkg] of arcEntries) {
31410
- arcImports[pkg] = `/shell/${short}.js`;
31411
- }
31454
+ init_i18n();
31455
+ import { existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
31456
+ import { join as join20 } from "path";
31457
+ function generateShellHtml(appName, manifest, initial) {
31458
+ const initialUrl = initial ? `/browser/${initial.file}` : null;
31459
+ if (!initialUrl) {
31460
+ throw new Error("generateShellHtml: initial bundle missing from manifest");
31412
31461
  }
31413
- const importMap = {
31414
- imports: {
31415
- react: "/shell/react.js",
31416
- "react/jsx-runtime": "/shell/jsx-runtime.js",
31417
- "react/jsx-dev-runtime": "/shell/jsx-dev-runtime.js",
31418
- "react-dom": "/shell/react-dom.js",
31419
- "react-dom/client": "/shell/react-dom-client.js",
31420
- ...arcImports
31421
- }
31422
- };
31423
31462
  return `<!doctype html>
31424
31463
  <html lang="en">
31425
31464
  <head>
@@ -31430,18 +31469,13 @@ function generateShellHtml(appName, manifest, arcEntries) {
31430
31469
  <link rel="manifest" href="/manifest.json">` : ""}
31431
31470
  <link rel="stylesheet" href="/styles.css" />
31432
31471
  <link rel="stylesheet" href="/theme.css" />
31433
- <script type="importmap">${JSON.stringify(importMap)}</script>
31472
+ <link rel="modulepreload" href="${initialUrl}" />
31434
31473
  </head>
31435
31474
  <body>
31436
31475
  <div id="root"></div>
31437
31476
  <script type="module">
31438
- import { createRoot } from "react-dom/client";
31439
- import { createElement } from "react";
31440
- import { PlatformApp } from "@arcote.tech/platform";
31441
-
31442
- createRoot(document.getElementById("root")).render(
31443
- createElement(PlatformApp)
31444
- );
31477
+ import { startApp } from "${initialUrl}";
31478
+ startApp("root");
31445
31479
  </script>
31446
31480
  </body>
31447
31481
  </html>`;
@@ -31466,28 +31500,39 @@ function getMime(path4) {
31466
31500
  return MIME[ext2] ?? "application/octet-stream";
31467
31501
  }
31468
31502
  function serveFile(filePath, headers = {}) {
31469
- if (!existsSync15(filePath))
31503
+ if (!existsSync18(filePath))
31470
31504
  return new Response("Not Found", { status: 404 });
31471
31505
  return new Response(Bun.file(filePath), {
31472
31506
  headers: { "Content-Type": getMime(filePath), ...headers }
31473
31507
  });
31474
31508
  }
31475
- var MODULE_SIG_SECRET = process.env.ARC_MODULE_SECRET ?? crypto.randomUUID();
31509
+ var MODULE_SIG_SECRET = process.env.ARC_MODULE_SECRET ?? "";
31476
31510
  var MODULE_SIG_TTL = 3600;
31477
- function signChunkUrl(chunk, file) {
31511
+ function ensureModuleSigSecret(ws, devMode) {
31512
+ if (MODULE_SIG_SECRET)
31513
+ return;
31514
+ if (devMode) {
31515
+ const hasher = new Bun.CryptoHasher("sha256");
31516
+ hasher.update(`arc-dev-secret:${ws.rootDir}`);
31517
+ MODULE_SIG_SECRET = hasher.digest("hex").slice(0, 32);
31518
+ } else {
31519
+ MODULE_SIG_SECRET = crypto.randomUUID();
31520
+ }
31521
+ }
31522
+ function signGroupUrl(file) {
31478
31523
  const exp = Math.floor(Date.now() / 1000) + MODULE_SIG_TTL;
31479
31524
  const hasher = new Bun.CryptoHasher("sha256");
31480
- hasher.update(`${chunk}/${file}:${exp}:${MODULE_SIG_SECRET}`);
31525
+ hasher.update(`${file}:${exp}:${MODULE_SIG_SECRET}`);
31481
31526
  const sig = hasher.digest("hex").slice(0, 16);
31482
- return `/modules/${chunk}/${file}?sig=${sig}&exp=${exp}`;
31527
+ return `/browser/${file}?sig=${sig}&exp=${exp}`;
31483
31528
  }
31484
- function verifyChunkSignature(chunk, file, sig, exp) {
31529
+ function verifyGroupSignature(file, sig, exp) {
31485
31530
  if (!sig || !exp)
31486
31531
  return false;
31487
31532
  if (Number(exp) < Date.now() / 1000)
31488
31533
  return false;
31489
31534
  const hasher = new Bun.CryptoHasher("sha256");
31490
- hasher.update(`${chunk}/${file}:${exp}:${MODULE_SIG_SECRET}`);
31535
+ hasher.update(`${file}:${exp}:${MODULE_SIG_SECRET}`);
31491
31536
  return hasher.digest("hex").slice(0, 16) === sig;
31492
31537
  }
31493
31538
  function decodeTokenPayload(jwt2) {
@@ -31522,90 +31567,85 @@ function parseArcTokensHeader(header) {
31522
31567
  return payloads;
31523
31568
  }
31524
31569
  async function filterManifestForTokens(manifest, moduleAccessMap, tokenPayloads) {
31525
- const allowedChunks = new Set(["public"]);
31570
+ const allowedGroups = new Set;
31526
31571
  for (const t of tokenPayloads) {
31527
31572
  if (t?.tokenType)
31528
- allowedChunks.add(t.tokenType);
31573
+ allowedGroups.add(t.tokenType);
31529
31574
  }
31530
- const filtered = [];
31531
- for (const mod of manifest.modules) {
31532
- if (!allowedChunks.has(mod.chunk))
31533
- continue;
31534
- if (mod.chunk === "public") {
31535
- filtered.push(mod);
31575
+ const filteredGroups = {};
31576
+ for (const [name, group] of Object.entries(manifest.groups)) {
31577
+ if (!allowedGroups.has(name))
31536
31578
  continue;
31537
- }
31538
- const access = moduleAccessMap.get(mod.name);
31539
- let granted = true;
31540
- if (access && access.rules.length > 0) {
31541
- granted = false;
31579
+ let allGranted = true;
31580
+ for (const moduleName of group.modules) {
31581
+ const access = moduleAccessMap.get(moduleName);
31582
+ if (!access || access.rules.length === 0)
31583
+ continue;
31584
+ let granted = false;
31542
31585
  for (const rule of access.rules) {
31543
- if (rule.token.name !== mod.chunk)
31586
+ if (rule.token.name !== name)
31544
31587
  continue;
31545
- const matching = tokenPayloads.find((t) => t.tokenType === rule.token.name);
31588
+ const matching = tokenPayloads.find((t) => t.tokenType === name);
31546
31589
  if (!matching)
31547
31590
  continue;
31548
31591
  granted = rule.check ? await rule.check(matching) : true;
31549
31592
  if (granted)
31550
31593
  break;
31551
31594
  }
31595
+ if (!granted) {
31596
+ allGranted = false;
31597
+ break;
31598
+ }
31552
31599
  }
31553
- if (granted) {
31554
- filtered.push({ ...mod, url: signChunkUrl(mod.chunk, mod.file) });
31600
+ if (allGranted) {
31601
+ filteredGroups[name] = { ...group, url: signGroupUrl(group.file) };
31555
31602
  }
31556
31603
  }
31557
31604
  return {
31558
- modules: filtered,
31559
- chunks: manifest.chunks,
31560
- shellHash: manifest.shellHash,
31605
+ initial: manifest.initial,
31606
+ groups: filteredGroups,
31607
+ sharedChunks: manifest.sharedChunks,
31561
31608
  stylesHash: manifest.stylesHash,
31562
31609
  buildTime: manifest.buildTime
31563
31610
  };
31564
31611
  }
31565
- var CHUNK_NAME_RE = /^[A-Za-z0-9_-]+$/;
31566
- var MODULE_FILE_RE = /^[A-Za-z0-9_.-]+\.js$/;
31567
- function staticFilesHandler(ws, devMode) {
31612
+ var BROWSER_FILE_RE = /^[A-Za-z0-9_.-]+\.js$/;
31613
+ function staticFilesHandler(ws, devMode, getManifest) {
31568
31614
  return (_req, url, ctx) => {
31569
31615
  const path4 = url.pathname;
31570
- if (path4.startsWith("/shell/"))
31571
- return serveFile(join18(ws.shellDir, path4.slice(7)), ctx.corsHeaders);
31572
- if (path4.startsWith("/modules/")) {
31573
- const rest = path4.slice(9);
31574
- const slash = rest.indexOf("/");
31575
- if (slash <= 0) {
31576
- return new Response("Not Found", { status: 404, headers: ctx.corsHeaders });
31577
- }
31578
- const chunk = rest.slice(0, slash);
31579
- const file = rest.slice(slash + 1);
31580
- if (!CHUNK_NAME_RE.test(chunk) || !MODULE_FILE_RE.test(file)) {
31616
+ if (path4.startsWith("/browser/")) {
31617
+ const file = path4.slice(9);
31618
+ if (!BROWSER_FILE_RE.test(file)) {
31581
31619
  return new Response("Not Found", { status: 404, headers: ctx.corsHeaders });
31582
31620
  }
31583
- if (chunk !== "public") {
31621
+ const manifest = getManifest();
31622
+ const isGroupEntry = Object.values(manifest.groups).some((g3) => g3.file === file);
31623
+ if (isGroupEntry) {
31584
31624
  const sig = url.searchParams.get("sig");
31585
31625
  const exp = url.searchParams.get("exp");
31586
- if (!verifyChunkSignature(chunk, file, sig, exp)) {
31626
+ if (!verifyGroupSignature(file, sig, exp)) {
31587
31627
  return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
31588
31628
  }
31589
31629
  }
31590
- return serveFile(join18(ws.modulesDir, chunk, file), {
31630
+ return serveFile(join20(ws.browserDir, file), {
31591
31631
  ...ctx.corsHeaders,
31592
31632
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
31593
31633
  });
31594
31634
  }
31595
31635
  if (path4.startsWith("/locales/"))
31596
- return serveFile(join18(ws.arcDir, path4.slice(1)), ctx.corsHeaders);
31636
+ return serveFile(join20(ws.arcDir, path4.slice(1)), ctx.corsHeaders);
31597
31637
  if (path4.startsWith("/assets/"))
31598
- return serveFile(join18(ws.assetsDir, path4.slice(8)), ctx.corsHeaders);
31638
+ return serveFile(join20(ws.assetsDir, path4.slice(8)), ctx.corsHeaders);
31599
31639
  if (path4 === "/styles.css")
31600
- return serveFile(join18(ws.arcDir, "styles.css"), ctx.corsHeaders);
31640
+ return serveFile(join20(ws.arcDir, "styles.css"), ctx.corsHeaders);
31601
31641
  if (path4 === "/theme.css")
31602
- return serveFile(join18(ws.arcDir, "theme.css"), ctx.corsHeaders);
31642
+ return serveFile(join20(ws.arcDir, "theme.css"), ctx.corsHeaders);
31603
31643
  if ((path4 === "/manifest.json" || path4 === "/manifest.webmanifest") && ws.manifest) {
31604
31644
  return serveFile(ws.manifest.path, ctx.corsHeaders);
31605
31645
  }
31606
31646
  if (path4.lastIndexOf(".") > path4.lastIndexOf("/")) {
31607
- const publicFile = join18(ws.publicDir, path4.slice(1));
31608
- if (existsSync15(publicFile))
31647
+ const publicFile = join20(ws.publicDir, path4.slice(1));
31648
+ if (existsSync18(publicFile))
31609
31649
  return serveFile(publicFile, ctx.corsHeaders);
31610
31650
  }
31611
31651
  return null;
@@ -31630,7 +31670,7 @@ function apiEndpointsHandler(ws, getManifest, cm, moduleAccessMap) {
31630
31670
  if (url.pathname === "/health") {
31631
31671
  return Response.json({
31632
31672
  status: "ok",
31633
- modules: getManifest().modules.length,
31673
+ groups: Object.keys(getManifest().groups).length,
31634
31674
  clients: cm?.clientCount ?? 0
31635
31675
  }, { headers: ctx.corsHeaders });
31636
31676
  }
@@ -31662,22 +31702,23 @@ function devReloadHandler(sseClients) {
31662
31702
  });
31663
31703
  };
31664
31704
  }
31665
- function spaFallbackHandler(shellHtml) {
31705
+ function spaFallbackHandler(getShellHtml) {
31666
31706
  return (_req, _url, ctx) => {
31667
- return new Response(shellHtml, {
31707
+ return new Response(getShellHtml(), {
31668
31708
  headers: { ...ctx.corsHeaders, "Content-Type": "text/html" }
31669
31709
  });
31670
31710
  };
31671
31711
  }
31672
31712
  async function startPlatformServer(opts) {
31673
31713
  const { ws, port, devMode, context } = opts;
31714
+ ensureModuleSigSecret(ws, !!devMode);
31674
31715
  const moduleAccessMap = opts.moduleAccess ?? new Map;
31675
31716
  let manifest = opts.manifest;
31676
31717
  const getManifest = () => manifest;
31677
31718
  const setManifest = (m4) => {
31678
31719
  manifest = m4;
31679
31720
  };
31680
- const shellHtml = generateShellHtml(ws.appName, ws.manifest, opts.arcEntries);
31721
+ const getShellHtml = () => generateShellHtml(ws.appName, ws.manifest, manifest?.initial);
31681
31722
  const sseClients = new Set;
31682
31723
  const notifyReload = (m4) => {
31683
31724
  const data = JSON.stringify(m4);
@@ -31714,8 +31755,8 @@ async function startPlatformServer(opts) {
31714
31755
  const handlers = [
31715
31756
  apiEndpointsHandler(ws, getManifest, null, moduleAccessMap),
31716
31757
  devReloadHandler(sseClients),
31717
- staticFilesHandler(ws, !!devMode),
31718
- spaFallbackHandler(shellHtml)
31758
+ staticFilesHandler(ws, !!devMode, getManifest),
31759
+ spaFallbackHandler(getShellHtml)
31719
31760
  ];
31720
31761
  for (const handler of handlers) {
31721
31762
  const response = await handler(req, url, ctx);
@@ -31735,7 +31776,7 @@ async function startPlatformServer(opts) {
31735
31776
  };
31736
31777
  }
31737
31778
  const { createBunSQLiteAdapterFactory: createBunSQLiteAdapterFactory2 } = await Promise.resolve().then(() => (init_dist(), exports_dist2));
31738
- const dbPath = opts.dbPath || join18(ws.arcDir, "data", "arc.db");
31779
+ const dbPath = opts.dbPath || join20(ws.arcDir, "data", "arc.db");
31739
31780
  const dbDir = dbPath.substring(0, dbPath.lastIndexOf("/"));
31740
31781
  if (dbDir)
31741
31782
  mkdirSync14(dbDir, { recursive: true });
@@ -31746,8 +31787,8 @@ async function startPlatformServer(opts) {
31746
31787
  httpHandlers: [
31747
31788
  apiEndpointsHandler(ws, getManifest, null, moduleAccessMap),
31748
31789
  devReloadHandler(sseClients),
31749
- staticFilesHandler(ws, !!devMode),
31750
- spaFallbackHandler(shellHtml)
31790
+ staticFilesHandler(ws, !!devMode, getManifest),
31791
+ spaFallbackHandler(getShellHtml)
31751
31792
  ],
31752
31793
  onWsClose: (clientId) => cleanupClientSubs(clientId)
31753
31794
  });
@@ -31765,17 +31806,17 @@ async function startPlatformServer(opts) {
31765
31806
  async function startPlatform(opts) {
31766
31807
  const { ws, devMode } = opts;
31767
31808
  const port = opts.port ?? parseInt(process.env.PORT || "5005", 10);
31768
- const dbPath = opts.dbPath ?? join19(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
31809
+ const dbPath = opts.dbPath ?? join21(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
31769
31810
  let manifest;
31770
31811
  if (devMode) {
31771
31812
  manifest = await buildAll(ws);
31772
31813
  } else {
31773
- const manifestPath = join19(ws.modulesDir, "manifest.json");
31774
- if (!existsSync16(manifestPath)) {
31814
+ const manifestPath = join21(ws.arcDir, "manifest.json");
31815
+ if (!existsSync19(manifestPath)) {
31775
31816
  err("No build found. Run `arc platform build` first.");
31776
31817
  process.exit(1);
31777
31818
  }
31778
- manifest = JSON.parse(readFileSync14(manifestPath, "utf-8"));
31819
+ manifest = JSON.parse(readFileSync15(manifestPath, "utf-8"));
31779
31820
  }
31780
31821
  log2("Loading server context...");
31781
31822
  const { context, moduleAccess } = await loadServerContext(ws);
@@ -31784,7 +31825,6 @@ async function startPlatform(opts) {
31784
31825
  } else {
31785
31826
  log2("No context \u2014 server endpoints skipped");
31786
31827
  }
31787
- const arcEntries = collectArcPeerDeps(ws.packages);
31788
31828
  const platform3 = await startPlatformServer({
31789
31829
  ws,
31790
31830
  port,
@@ -31792,8 +31832,7 @@ async function startPlatform(opts) {
31792
31832
  context,
31793
31833
  moduleAccess,
31794
31834
  dbPath,
31795
- devMode,
31796
- arcEntries
31835
+ devMode
31797
31836
  });
31798
31837
  ok(`Server on http://localhost:${port}`);
31799
31838
  if (platform3.contextHandler) {
@@ -31825,7 +31864,7 @@ function attachDevWatcher(ws, platform3) {
31825
31864
  const next = await buildAll(ws);
31826
31865
  platform3.setManifest(next);
31827
31866
  platform3.notifyReload(next);
31828
- ok(`Rebuilt \u2014 ${next.modules.length} module(s)`);
31867
+ ok(`Rebuilt \u2014 initial + ${Object.keys(next.groups).length} group(s)`);
31829
31868
  } catch (e2) {
31830
31869
  console.error(`Rebuild failed: ${e2}`);
31831
31870
  } finally {
@@ -31834,8 +31873,8 @@ function attachDevWatcher(ws, platform3) {
31834
31873
  }, 300);
31835
31874
  };
31836
31875
  for (const pkg of ws.packages) {
31837
- const srcDir = join19(pkg.path, "src");
31838
- if (!existsSync16(srcDir))
31876
+ const srcDir = join21(pkg.path, "src");
31877
+ if (!existsSync19(srcDir))
31839
31878
  continue;
31840
31879
  watch(srcDir, { recursive: true }, (_event, filename) => {
31841
31880
  if (!filename || filename.includes(".arc") || filename.endsWith(".d.ts") || filename.includes("node_modules") || filename.includes("dist"))
@@ -31845,8 +31884,8 @@ function attachDevWatcher(ws, platform3) {
31845
31884
  triggerRebuild();
31846
31885
  });
31847
31886
  }
31848
- const localesDir = join19(ws.rootDir, "locales");
31849
- if (existsSync16(localesDir)) {
31887
+ const localesDir = join21(ws.rootDir, "locales");
31888
+ if (existsSync19(localesDir)) {
31850
31889
  watch(localesDir, { recursive: false }, (_event, filename) => {
31851
31890
  if (!filename?.endsWith(".po"))
31852
31891
  return;
@@ -31876,7 +31915,7 @@ var platform3 = program2.command("platform").description("Platform commands \u20
31876
31915
  platform3.command("dev").description("Start platform in dev mode (Bun server + Vite HMR)").option("--no-cache", "Force full rebuild on startup").action((opts) => platformDev({ noCache: opts.cache === false }));
31877
31916
  platform3.command("build").description("Build platform for production").option("--no-cache", "Force full rebuild").action((opts) => platformBuild({ noCache: opts.cache === false }));
31878
31917
  platform3.command("start").description("Start platform in production mode (requires prior build)").action(platformStart);
31879
- platform3.command("deploy [env]").description("Deploy platform to a remote server (reads deploy.arc.json, surveys if missing)").option("--skip-build", "Skip local build step").option("--rebuild", "Force rebuild before deploy").option("--build-only", "Build the Docker image locally, then exit (no remote push)").option("--image-tag <hash>", "Roll back / pin to an existing image tag instead of building a new one").action((env2, opts) => platformDeploy(env2, opts));
31918
+ platform3.command("deploy [env]").description("Deploy platform to a remote server (reads deploy.arc.json, surveys if missing)").option("--skip-build", "Skip local build step").option("--rebuild", "Force rebuild before deploy").option("--build-only", "Build the Docker image locally, then exit (no remote push)").option("--image-tag <hash>", "Roll back / pin to an existing image tag instead of building a new one").option("--force-bootstrap", "Re-run Ansible host bootstrap even if the server is already configured").action((env2, opts) => platformDeploy(env2, opts));
31880
31919
  program2.parse(process.argv);
31881
31920
  if (process.argv.length === 2) {
31882
31921
  program2.help();