@arcote.tech/arc-cli 0.7.0 → 0.7.2

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
  }
@@ -26319,318 +26598,49 @@ ${colors3.yellow}Type declaration errors:${colors3.reset}`);
26319
26598
  devContexts.push({
26320
26599
  configPath,
26321
26600
  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));
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,9 @@ 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 isBrowser2 = client.name === "browser";
26946
+ const workspaceDeps = isBrowser2 ? Object.keys(allDeps) : Object.entries(allDeps).filter(([, spec]) => !spec.startsWith("workspace:")).map(([name]) => name);
26947
+ const externals = [...peerDeps, ...workspaceDeps];
26919
26948
  const result = await Bun.build({
26920
26949
  entrypoints: [pkg.entrypoint],
26921
26950
  outdir: join8(outDir, "main"),
@@ -26944,9 +26973,39 @@ async function buildContextPackages(rootDir, packages, cache, noCache) {
26944
26973
  const contexts = packages.filter((p) => isContextPackage(p.packageJson));
26945
26974
  if (contexts.length === 0)
26946
26975
  return { declarationErrors: [] };
26947
- const tasks = contexts.flatMap((pkg) => CONTEXT_CLIENTS.map((client) => () => buildContextClient(pkg, rootDir, client, cache, noCache)));
26948
- const results = await pAll(tasks);
26949
- const declarationErrors = results.flatMap((r) => r.declarationErrors);
26976
+ const byName = new Map(contexts.map((p) => [p.name, p]));
26977
+ const remaining = new Set(contexts.map((p) => p.name));
26978
+ const done = new Set;
26979
+ const ordered = [];
26980
+ const workspaceDepsOf = (pkg) => {
26981
+ const deps = pkg.packageJson.dependencies ?? {};
26982
+ return Object.entries(deps).filter(([name, spec]) => spec.startsWith("workspace:") && byName.has(name)).map(([name]) => name);
26983
+ };
26984
+ while (remaining.size > 0) {
26985
+ const layer = [];
26986
+ for (const name of remaining) {
26987
+ const pkg = byName.get(name);
26988
+ const unmetDeps = workspaceDepsOf(pkg).filter((d) => !done.has(d));
26989
+ if (unmetDeps.length === 0)
26990
+ layer.push(pkg);
26991
+ }
26992
+ if (layer.length === 0) {
26993
+ const cycle = [...remaining].join(", ");
26994
+ throw new Error(`Workspace dependency cycle detected: ${cycle}`);
26995
+ }
26996
+ ordered.push(layer);
26997
+ for (const pkg of layer) {
26998
+ done.add(pkg.name);
26999
+ remaining.delete(pkg.name);
27000
+ }
27001
+ }
27002
+ const declarationErrors = [];
27003
+ for (const layer of ordered) {
27004
+ const tasks = layer.flatMap((pkg) => CONTEXT_CLIENTS.map((client) => () => buildContextClient(pkg, rootDir, client, cache, noCache)));
27005
+ const results = await pAll(tasks);
27006
+ for (const r of results)
27007
+ declarationErrors.push(...r.declarationErrors);
27008
+ }
26950
27009
  if (declarationErrors.length > 0) {
26951
27010
  console.warn(`
26952
27011
  \x1B[33mType declaration errors:\x1B[0m`);
@@ -26956,125 +27015,163 @@ async function buildContextPackages(rootDir, packages, cache, noCache) {
26956
27015
  }
26957
27016
  return { declarationErrors };
26958
27017
  }
26959
- async function buildModulesByChunks(rootDir, outDir, plan, cache, noCache) {
27018
+ async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollector) {
26960
27019
  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
- }));
27020
+ const publicMembers = plan.groups.get("public") ?? [];
27021
+ const protectedGroups = plan.chunks.filter((c) => c !== "public").map((c) => ({ name: c, members: plan.groups.get(c) ?? [] })).filter((g) => g.members.length > 0);
27022
+ const unitId = "browser-app";
27023
+ const allMembers = [];
27024
+ for (const m of publicMembers) {
27025
+ allMembers.push({ name: m.pkg.name, group: "public", srcHash: pkgSourceHash(m.pkg) });
27026
+ }
27027
+ for (const g of protectedGroups) {
27028
+ for (const m of g.members) {
27029
+ allMembers.push({ name: m.pkg.name, group: g.name, srcHash: pkgSourceHash(m.pkg) });
27030
+ }
27031
+ }
26986
27032
  const inputHash = sha256OfJson({
26987
- chunk,
26988
- pkgHashes,
26989
- externals: SHELL_EXTERNALS,
27033
+ members: allMembers,
27034
+ groups: [
27035
+ "initial",
27036
+ ...protectedGroups.map((g) => g.name).sort()
27037
+ ],
26990
27038
  define: { ONLY_SERVER: "false", ONLY_BROWSER: "true", ONLY_CLIENT: "true" }
26991
27039
  });
26992
27040
  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
- });
27041
+ const cached = cache.units[unitId]?.outputHashes;
27042
+ if (cached?._manifest) {
27043
+ try {
27044
+ const m = JSON.parse(cached._manifest);
27045
+ const allFiles = [
27046
+ m.initial.file,
27047
+ ...Object.values(m.groups).map((g) => g.file),
27048
+ ...m.sharedChunks
27049
+ ];
27050
+ if (allFiles.every((f) => existsSync7(join8(outDir, f)))) {
27051
+ console.log(` \u2713 cached: ${unitId}`);
27052
+ return { ...m, cached: true };
27053
+ }
27054
+ } catch {}
27009
27055
  }
27010
- if (!missing) {
27011
- console.log(` \u2713 cached: ${unitId} (${modules2.length} module(s))`);
27012
- return { modules: modules2, cached: true };
27056
+ }
27057
+ console.log(` building: ${unitId} (initial: ${publicMembers.length} modules, groups: ${protectedGroups.map((g) => `${g.name}=${g.members.length}`).join(",") || "none"})`);
27058
+ if (existsSync7(outDir)) {
27059
+ for (const f of readdirSync4(outDir)) {
27060
+ if (f.endsWith(".js"))
27061
+ rmSync(join8(outDir, f), { force: true });
27013
27062
  }
27014
- console.log(` rebuilding ${unitId}: output file missing`);
27015
27063
  }
27016
- console.log(` building: ${unitId} (${members.length} module(s))`);
27017
- const tmpDir = join8(chunkOutDir, "_entries");
27064
+ const tmpDir = join8(outDir, "_entries");
27018
27065
  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}";
27066
+ const importLines = (pkgs) => pkgs.map((m) => `import "${m.pkg.name}";`).join(`
27067
+ `);
27068
+ const initialEntry = join8(tmpDir, "initial.ts");
27069
+ writeFileSync6(initialEntry, `${importLines(publicMembers)}
27070
+ export { startApp } from "@arcote.tech/platform";
27071
+ `);
27072
+ const entryPaths = [initialEntry];
27073
+ const groupModuleMap = new Map;
27074
+ for (const g of protectedGroups) {
27075
+ const entry = join8(tmpDir, `${g.name}.ts`);
27076
+ writeFileSync6(entry, `${importLines(g.members)}
27077
+ `);
27078
+ entryPaths.push(entry);
27079
+ groupModuleMap.set(g.name, g.members.map((m) => m.moduleName));
27080
+ }
27081
+ const allMemberPkgs = new Map;
27082
+ for (const m of publicMembers)
27083
+ allMemberPkgs.set(m.pkg.name, m.pkg);
27084
+ for (const g of protectedGroups)
27085
+ for (const m of g.members)
27086
+ allMemberPkgs.set(m.pkg.name, m.pkg);
27087
+ const patchedPkgJsons = [];
27088
+ for (const pkg of allMemberPkgs.values()) {
27089
+ const pkgJsonPath = join8(pkg.path, "package.json");
27090
+ if (!existsSync7(pkgJsonPath))
27091
+ continue;
27092
+ const original = readFileSync7(pkgJsonPath, "utf-8");
27093
+ const parsed = JSON.parse(original);
27094
+ if (parsed.sideEffects === true)
27095
+ continue;
27096
+ parsed.sideEffects = true;
27097
+ writeFileSync6(pkgJsonPath, JSON.stringify(parsed, null, 2) + `
27025
27098
  `);
27026
- entrypoints.push(wrapperFile);
27099
+ patchedPkgJsons.push({ path: pkgJsonPath, original });
27027
27100
  }
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
- });
27101
+ let result;
27102
+ try {
27103
+ result = await Bun.build({
27104
+ entrypoints: entryPaths,
27105
+ outdir: outDir,
27106
+ splitting: true,
27107
+ format: "esm",
27108
+ target: "browser",
27109
+ external: [],
27110
+ plugins: [
27111
+ singleReactPlugin(rootDir),
27112
+ jsxDevShimPlugin(),
27113
+ i18nExtractPlugin(i18nCollector, rootDir)
27114
+ ],
27115
+ naming: "[name].[ext]",
27116
+ define: {
27117
+ ONLY_SERVER: "false",
27118
+ ONLY_BROWSER: "true",
27119
+ ONLY_CLIENT: "true",
27120
+ "process.env.NODE_ENV": '"production"'
27121
+ }
27122
+ });
27123
+ } finally {
27124
+ for (const p of patchedPkgJsons)
27125
+ writeFileSync6(p.path, p.original);
27126
+ }
27127
+ rmSync(tmpDir, { recursive: true, force: true });
27055
27128
  if (!result.success) {
27056
- console.error(`Chunk "${chunk}" build failed:`);
27057
27129
  for (const log2 of result.logs)
27058
27130
  console.error(log2);
27059
- throw new Error(`Module chunk build failed: ${chunk}`);
27131
+ throw new Error("Browser app build failed");
27132
+ }
27133
+ let initialFile = "";
27134
+ let initialHash = "";
27135
+ const groups = {};
27136
+ const sharedChunks = [];
27137
+ for (const out of result.outputs) {
27138
+ const name = basename2(out.path);
27139
+ if (out.kind === "entry-point") {
27140
+ const bytes = readFileSync7(out.path);
27141
+ const hash = sha256Hex(bytes).slice(0, 16);
27142
+ const stem = name.replace(/\.js$/, "");
27143
+ const finalName = `${stem}.${hash}.js`;
27144
+ const finalPath = join8(outDir, finalName);
27145
+ rmSync(finalPath, { force: true });
27146
+ writeFileSync6(finalPath, bytes);
27147
+ rmSync(out.path, { force: true });
27148
+ if (stem === "initial") {
27149
+ initialFile = finalName;
27150
+ initialHash = hash;
27151
+ } else {
27152
+ groups[stem] = {
27153
+ file: finalName,
27154
+ hash,
27155
+ modules: groupModuleMap.get(stem) ?? []
27156
+ };
27157
+ }
27158
+ } else if (out.kind === "chunk") {
27159
+ sharedChunks.push(name);
27160
+ }
27060
27161
  }
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
- };
27162
+ if (!initialFile) {
27163
+ throw new Error("Browser app build: initial entry not found in outputs");
27164
+ }
27165
+ const manifest = {
27166
+ initial: { file: initialFile, hash: initialHash },
27167
+ groups,
27168
+ sharedChunks,
27169
+ cached: false
27170
+ };
27171
+ updateCache(cache, unitId, inputHash, {
27172
+ outputHashes: { _manifest: JSON.stringify(manifest) }
27075
27173
  });
27076
- updateCache(cache, unitId, inputHash, { outputHashes });
27077
- return { modules, cached: false };
27174
+ return manifest;
27078
27175
  }
27079
27176
  async function buildTranslations(rootDir, arcDir, cache, noCache) {
27080
27177
  const localesDir = join8(rootDir, "locales");
@@ -27216,17 +27313,10 @@ import {
27216
27313
  existsSync as existsSync8,
27217
27314
  mkdirSync as mkdirSync7,
27218
27315
  readFileSync as readFileSync8,
27219
- realpathSync as realpathSync2,
27220
27316
  unlinkSync as unlinkSync2,
27221
27317
  writeFileSync as writeFileSync7
27222
27318
  } 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
- }
27319
+ import { join as join9 } from "path";
27230
27320
  async function extractAccessMap(rootDir, packages) {
27231
27321
  const serverBundles = packages.filter((p) => isContextPackage(p.packageJson)).map((p) => ({
27232
27322
  name: p.name,
@@ -27244,8 +27334,7 @@ async function extractAccessMap(rootDir, packages) {
27244
27334
  env: {
27245
27335
  ...process.env,
27246
27336
  ARC_ACCESS_BUNDLES: JSON.stringify(serverBundles),
27247
- ARC_ACCESS_OUT: outPath,
27248
- ARC_PLATFORM_ENTRY: locatePlatformServerEntry()
27337
+ ARC_ACCESS_OUT: outPath
27249
27338
  },
27250
27339
  stdout: "pipe",
27251
27340
  stderr: "inherit"
@@ -27276,9 +27365,11 @@ if (!out) {
27276
27365
  process.exit(2);
27277
27366
  }
27278
27367
 
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);
27368
+ // Bare-specifier import \u2014 Bun walks up from this worker's location
27369
+ // (<rootDir>/.arc/.tmp/) to <rootDir>/node_modules and finds the package.
27370
+ // Single entry (./src/index.ts) \u2014 React imports on top level are benign
27371
+ // (createContext, function defs); no DOM access until actual render.
27372
+ const platform = await import("@arcote.tech/platform");
27282
27373
 
27283
27374
  for (const { name, path } of bundles) {
27284
27375
  try {
@@ -27436,7 +27527,7 @@ function resolveWorkspace() {
27436
27527
  err("No package.json found");
27437
27528
  process.exit(1);
27438
27529
  }
27439
- const rootDir = dirname7(packageJsonPath);
27530
+ const rootDir = dirname6(packageJsonPath);
27440
27531
  const rootPkg = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
27441
27532
  const appName = rootPkg.name ?? "Arc App";
27442
27533
  const arcDir = join11(rootDir, ".arc", "platform");
@@ -27471,8 +27562,7 @@ function resolveWorkspace() {
27471
27562
  rootPkg,
27472
27563
  appName,
27473
27564
  arcDir,
27474
- modulesDir: join11(arcDir, "modules"),
27475
- shellDir: join11(arcDir, "shell"),
27565
+ browserDir: join11(arcDir, "browser"),
27476
27566
  assetsDir: join11(arcDir, "assets"),
27477
27567
  publicDir: join11(rootDir, "public"),
27478
27568
  packages,
@@ -27492,32 +27582,27 @@ async function buildAll(ws, opts = {}) {
27492
27582
  `);
27493
27583
  const plan = planChunks(ws.packages, accessMap);
27494
27584
  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),
27585
+ const i18nCollector = new Map;
27586
+ const [browserResult] = await Promise.all([
27587
+ buildBrowserApp(ws.rootDir, ws.browserDir, plan, cache, noCache, i18nCollector),
27498
27588
  buildStyles(ws.rootDir, ws.arcDir, ws.packages, themePath, cache, noCache),
27499
27589
  copyBrowserAssets(ws, cache, noCache),
27500
27590
  buildTranslations(ws.rootDir, ws.arcDir, cache, noCache)
27501
27591
  ]);
27592
+ const { finalizeTranslations: finalizeTranslations3 } = await Promise.resolve().then(() => (init_i18n(), exports_i18n));
27593
+ await finalizeTranslations3(ws.rootDir, ws.arcDir, i18nCollector);
27502
27594
  collectFrameworkDeps(ws.arcDir, ws.rootDir, ws.packages);
27503
27595
  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));
27596
+ const finalManifest = assembleManifest(ws, browserResult, cache);
27597
+ writeFileSync9(join11(ws.arcDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
27506
27598
  return finalManifest;
27507
27599
  }
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);
27600
+ function assembleManifest(ws, browser, cache) {
27516
27601
  const stylesHash = cache.units["styles"]?.outputHash ?? "";
27517
27602
  return {
27518
- modules,
27519
- chunks,
27520
- shellHash,
27603
+ initial: browser.initial,
27604
+ groups: browser.groups,
27605
+ sharedChunks: browser.sharedChunks,
27521
27606
  stylesHash,
27522
27607
  buildTime: new Date().toISOString()
27523
27608
  };
@@ -27619,170 +27704,12 @@ async function copyBrowserAssets(ws, cache, noCache) {
27619
27704
  const outputHashes = {};
27620
27705
  for (const asset of assets) {
27621
27706
  const dest = join11(ws.assetsDir, asset.to);
27622
- mkdirSync9(dirname7(dest), { recursive: true });
27707
+ mkdirSync9(dirname6(dest), { recursive: true });
27623
27708
  copyFileSync(asset.src, dest);
27624
27709
  outputHashes[asset.to] = sha256Hex(readFileSync10(dest));
27625
27710
  }
27626
27711
  updateCache(cache, unitId, inputHash, { outputHashes });
27627
27712
  }
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
27713
  async function loadServerContext(ws) {
27787
27714
  globalThis.ONLY_SERVER = true;
27788
27715
  globalThis.ONLY_BROWSER = false;
@@ -27830,24 +27757,25 @@ async function loadServerContext(ws) {
27830
27757
  async function platformBuild(opts = {}) {
27831
27758
  const ws = resolveWorkspace();
27832
27759
  const manifest = await buildAll(ws, { noCache: opts.noCache });
27833
- ok(`Platform built \u2014 ${manifest.modules.length} module(s)`);
27760
+ const groupCount = Object.keys(manifest.groups).length;
27761
+ ok(`Platform built \u2014 initial + ${groupCount} group(s)`);
27834
27762
  }
27835
27763
 
27836
27764
  // 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";
27765
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
27766
+ import { dirname as dirname8, join as join19 } from "path";
27767
+ import { fileURLToPath as fileURLToPath6 } from "url";
27840
27768
 
27841
27769
  // src/deploy/bootstrap.ts
27842
27770
  var {spawn: spawn4 } = globalThis.Bun;
27843
27771
  import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync13 } from "fs";
27844
27772
  import { tmpdir as tmpdir2 } from "os";
27845
- import { join as join15 } from "path";
27773
+ import { join as join17 } from "path";
27846
27774
 
27847
27775
  // src/deploy/ansible.ts
27848
27776
  import { spawn as nodeSpawn } from "child_process";
27849
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
27850
- import { tmpdir } from "os";
27777
+ import { existsSync as existsSync11, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
27778
+ import { homedir, tmpdir } from "os";
27851
27779
  import { join as join12 } from "path";
27852
27780
 
27853
27781
  // src/deploy/assets.ts
@@ -28042,7 +27970,22 @@ var ANSIBLE_SITE_YML = `---
28042
27970
  - { policy: deny, dir: incoming }
28043
27971
  - { policy: allow, dir: outgoing }
28044
27972
 
28045
- - name: Open firewall ports
27973
+ - name: Remove legacy ufw limit rule on SSH (replaced by plain allow)
27974
+ # If a prior bootstrap installed \`ufw limit 22/tcp\`, drop it \u2014 otherwise
27975
+ # the limit rule shadows the allow rule and rate-throttles deploy flows.
27976
+ ufw:
27977
+ rule: limit
27978
+ port: "{{ ssh_port }}"
27979
+ proto: tcp
27980
+ delete: true
27981
+ ignore_errors: true
27982
+
27983
+ - name: Open firewall ports (SSH key-only auth, no brute-force surface)
27984
+ # SSH on port 22: PasswordAuthentication=no + key-only means brute force
27985
+ # is impossible without the operator's private key. Rate-limiting (ufw
27986
+ # limit / fail2ban sshd jail) breaks legitimate deploy flows that open
27987
+ # many short SSH connections in sequence (canSsh -> sshExec -> scp -> ...).
27988
+ # 80/443: Caddy ACME + app traffic, never rate-limited.
28046
27989
  ufw:
28047
27990
  rule: allow
28048
27991
  port: "{{ item }}"
@@ -28056,17 +27999,18 @@ var ANSIBLE_SITE_YML = `---
28056
27999
  ufw:
28057
28000
  state: enabled
28058
28001
 
28059
- - name: Configure fail2ban for sshd
28002
+ - name: Disable fail2ban sshd jail
28003
+ # Key-only SSH + ufw rate-limit make fail2ban for sshd redundant and
28004
+ # actively harmful when the operator's IP roams. Keep fail2ban installed
28005
+ # for future jails (web/db) but turn off the sshd jail explicitly.
28060
28006
  copy:
28061
28007
  dest: /etc/fail2ban/jail.local
28062
28008
  content: |
28063
28009
  [sshd]
28064
- enabled = true
28065
- port = {{ ssh_port }}
28066
- maxretry = 5
28067
- findtime = 600
28068
- bantime = 3600
28010
+ enabled = false
28011
+ {% if extra_allowed_ips %}
28069
28012
  ignoreip = 127.0.0.1/8 ::1 {{ extra_allowed_ips | join(' ') }}
28013
+ {% endif %}
28070
28014
  mode: "0644"
28071
28015
  notify: restart fail2ban
28072
28016
 
@@ -28114,18 +28058,32 @@ async function materializeAssets(targetDir, files) {
28114
28058
  }
28115
28059
 
28116
28060
  // src/deploy/ansible.ts
28061
+ function pickSshKeyForAnsible(configured) {
28062
+ if (configured) {
28063
+ const expanded = configured.startsWith("~") ? join12(homedir(), configured.slice(1)) : configured;
28064
+ return existsSync11(expanded) ? expanded : null;
28065
+ }
28066
+ for (const name of ["id_ed25519", "id_ecdsa", "id_rsa"]) {
28067
+ const path4 = join12(homedir(), ".ssh", name);
28068
+ if (existsSync11(path4))
28069
+ return path4;
28070
+ }
28071
+ return null;
28072
+ }
28117
28073
  async function runAnsible(inputs) {
28118
28074
  const workDir = join12(tmpdir(), "arc-deploy", `ansible-${Date.now()}`);
28119
28075
  mkdirSync10(workDir, { recursive: true });
28120
28076
  await materializeAssets(workDir, ASSETS.ansible);
28121
28077
  const user = inputs.asRoot ? "root" : inputs.target.user;
28122
28078
  const port = inputs.ansible?.sshPort ?? inputs.target.port;
28079
+ const sshKey = pickSshKeyForAnsible(inputs.target.sshKey);
28080
+ const sshKeyArg = sshKey ? ` -o IdentitiesOnly=yes -i ${sshKey}` : "";
28123
28081
  const inventory = [
28124
28082
  "[arc]",
28125
28083
  `${inputs.target.host} ansible_user=${user} ansible_port=${port}`,
28126
28084
  "",
28127
28085
  "[arc:vars]",
28128
- "ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new -o BatchMode=yes'",
28086
+ `ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new -o BatchMode=yes -o PreferredAuthentications=publickey${sshKeyArg}'`,
28129
28087
  "ansible_python_interpreter=/usr/bin/python3",
28130
28088
  ""
28131
28089
  ].join(`
@@ -28224,7 +28182,7 @@ function generateCompose({ cfg }) {
28224
28182
  for (const [name, env2] of Object.entries(cfg.envs)) {
28225
28183
  const upperName = name.toUpperCase().replace(/-/g, "_");
28226
28184
  lines.push(` arc-${name}:`);
28227
- lines.push(` image: \${ARC_IMAGE_${upperName}:?Run \\\`arc platform deploy ${name}\\\` to publish an image first}`);
28185
+ lines.push(` image: \${ARC_IMAGE_${upperName}:-arc-${name}:not-deployed}`);
28228
28186
  lines.push(` container_name: arc-${name}`);
28229
28187
  lines.push(" restart: unless-stopped");
28230
28188
  lines.push(" volumes:");
@@ -28278,16 +28236,16 @@ async function generateHtpasswd(user, password) {
28278
28236
  // src/deploy/terraform.ts
28279
28237
  import { spawn as nodeSpawn2 } from "child_process";
28280
28238
  import { createHash as createHash2 } from "crypto";
28281
- import { existsSync as existsSync11, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
28282
- import { homedir } from "os";
28239
+ import { existsSync as existsSync12, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
28240
+ import { homedir as homedir2 } from "os";
28283
28241
  import { join as join13 } from "path";
28284
28242
  async function runTerraform(inputs) {
28285
28243
  const wsHash = createHash2("sha256").update(inputs.workspaceDir).digest("hex").slice(0, 16);
28286
- const workDir = join13(homedir(), ".arc-deploy", wsHash, "tf");
28244
+ const workDir = join13(homedir2(), ".arc-deploy", wsHash, "tf");
28287
28245
  mkdirSync11(workDir, { recursive: true });
28288
28246
  await materializeAssets(workDir, ASSETS.terraform);
28289
28247
  const sshPubKey = inputs.tf.sshPublicKey ?? expandHome("~/.ssh/id_ed25519.pub");
28290
- if (!existsSync11(expandHome(sshPubKey))) {
28248
+ if (!existsSync12(expandHome(sshPubKey))) {
28291
28249
  throw new Error(`SSH public key not found at ${sshPubKey}. Set provision.terraform.sshPublicKey in deploy.arc.json.`);
28292
28250
  }
28293
28251
  const tfvars = [
@@ -28358,33 +28316,93 @@ function expandHome(p) {
28358
28316
  }
28359
28317
 
28360
28318
  // src/deploy/config.ts
28361
- import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync12 } from "fs";
28319
+ import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
28320
+ import { join as join15 } from "path";
28321
+
28322
+ // src/deploy/env-file.ts
28323
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
28362
28324
  import { join as join14 } from "path";
28325
+ function loadDeployEnvFiles(rootDir, envNames) {
28326
+ const globalsPath = join14(rootDir, "deploy.arc.env");
28327
+ const globals = existsSync13(globalsPath) ? parseEnvFile(readFileSync11(globalsPath, "utf-8"), globalsPath) : {};
28328
+ const perEnv = {};
28329
+ for (const name of envNames) {
28330
+ const envPath = join14(rootDir, `deploy.arc.${name}.env`);
28331
+ if (existsSync13(envPath)) {
28332
+ perEnv[name] = parseEnvFile(readFileSync11(envPath, "utf-8"), envPath);
28333
+ }
28334
+ }
28335
+ return { globals, perEnv };
28336
+ }
28337
+ function applyDeployGlobals(globals) {
28338
+ for (const [k, v] of Object.entries(globals)) {
28339
+ if (process.env[k] === undefined) {
28340
+ process.env[k] = v;
28341
+ }
28342
+ }
28343
+ }
28344
+ function parseEnvFile(content, pathForErrors) {
28345
+ const out = {};
28346
+ const lines = content.split(/\r?\n/);
28347
+ for (let i = 0;i < lines.length; i++) {
28348
+ const raw = lines[i];
28349
+ const line = raw.trim();
28350
+ if (!line || line.startsWith("#"))
28351
+ continue;
28352
+ const eq = line.indexOf("=");
28353
+ if (eq <= 0) {
28354
+ throw new Error(`${pathForErrors}:${i + 1}: malformed line (expected KEY=VALUE): ${raw}`);
28355
+ }
28356
+ const key = line.slice(0, eq).trim();
28357
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
28358
+ throw new Error(`${pathForErrors}:${i + 1}: invalid variable name "${key}"`);
28359
+ }
28360
+ let value = line.slice(eq + 1).trim();
28361
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
28362
+ value = value.slice(1, -1);
28363
+ }
28364
+ out[key] = value;
28365
+ }
28366
+ return out;
28367
+ }
28368
+
28369
+ // src/deploy/config.ts
28363
28370
  var DEPLOY_CONFIG_FILE = "deploy.arc.json";
28364
28371
  function deployConfigPath(rootDir) {
28365
- return join14(rootDir, DEPLOY_CONFIG_FILE);
28372
+ return join15(rootDir, DEPLOY_CONFIG_FILE);
28366
28373
  }
28367
28374
  function deployConfigExists(rootDir) {
28368
- return existsSync12(deployConfigPath(rootDir));
28375
+ return existsSync14(deployConfigPath(rootDir));
28369
28376
  }
28370
28377
  function loadDeployConfig(rootDir) {
28371
28378
  const path4 = deployConfigPath(rootDir);
28372
- if (!existsSync12(path4)) {
28379
+ if (!existsSync14(path4)) {
28373
28380
  throw new Error(`Missing ${DEPLOY_CONFIG_FILE} at ${path4}`);
28374
28381
  }
28375
- const raw = readFileSync11(path4, "utf-8");
28382
+ const raw = readFileSync12(path4, "utf-8");
28376
28383
  let parsed;
28377
28384
  try {
28378
28385
  parsed = JSON.parse(raw);
28379
28386
  } catch (e) {
28380
28387
  throw new Error(`Invalid JSON in ${DEPLOY_CONFIG_FILE}: ${e.message}`);
28381
28388
  }
28389
+ const envNames = isObject(parsed) && isObject(parsed.envs) ? Object.keys(parsed.envs) : [];
28390
+ const envFiles = loadDeployEnvFiles(rootDir, envNames);
28391
+ applyDeployGlobals(envFiles.globals);
28382
28392
  const expanded = expandEnvVars(parsed, process.env);
28383
- return validateDeployConfig(expanded);
28393
+ const validated = validateDeployConfig(expanded);
28394
+ for (const [envName, vars] of Object.entries(envFiles.perEnv)) {
28395
+ if (!(envName in validated.envs))
28396
+ continue;
28397
+ const env2 = validated.envs[envName];
28398
+ const merged = { ...vars, ...env2.envVars ?? {} };
28399
+ validated.envs[envName] = { ...env2, envVars: merged };
28400
+ }
28401
+ return validated;
28384
28402
  }
28385
28403
  function saveDeployConfig(rootDir, cfg) {
28386
28404
  const path4 = deployConfigPath(rootDir);
28387
- const raw = existsSync12(path4) ? JSON.parse(readFileSync11(path4, "utf-8")) : {};
28405
+ const raw = existsSync14(path4) ? JSON.parse(readFileSync12(path4, "utf-8")) : {};
28388
28406
  raw.target = { ...raw.target, ...cfg.target };
28389
28407
  writeFileSync12(path4, JSON.stringify(raw, null, 2) + `
28390
28408
  `);
@@ -28552,22 +28570,47 @@ function cfgErr(path4, expected) {
28552
28570
 
28553
28571
  // src/deploy/ssh.ts
28554
28572
  var {spawn: spawn3 } = globalThis.Bun;
28573
+ import { existsSync as existsSync15 } from "fs";
28574
+ import { homedir as homedir3 } from "os";
28575
+ import { join as join16 } from "path";
28576
+ function pickSshKey(target) {
28577
+ if (target.sshKey) {
28578
+ const expanded = target.sshKey.startsWith("~") ? join16(homedir3(), target.sshKey.slice(1)) : target.sshKey;
28579
+ return existsSync15(expanded) ? expanded : null;
28580
+ }
28581
+ for (const name of ["id_ed25519", "id_ecdsa", "id_rsa"]) {
28582
+ const path4 = join16(homedir3(), ".ssh", name);
28583
+ if (existsSync15(path4))
28584
+ return path4;
28585
+ }
28586
+ return null;
28587
+ }
28555
28588
  async function streamToString(stream2) {
28556
28589
  if (!stream2 || typeof stream2 === "number")
28557
28590
  return "";
28558
28591
  return new Response(stream2).text();
28559
28592
  }
28560
28593
  function baseSshArgs(target) {
28594
+ const key = pickSshKey(target);
28561
28595
  const args = [
28562
28596
  "-o",
28563
28597
  "BatchMode=yes",
28564
28598
  "-o",
28565
28599
  "StrictHostKeyChecking=accept-new",
28600
+ "-o",
28601
+ "PreferredAuthentications=publickey",
28602
+ "-o",
28603
+ "ConnectTimeout=5",
28604
+ "-o",
28605
+ "ServerAliveInterval=10",
28606
+ "-o",
28607
+ "ServerAliveCountMax=2",
28566
28608
  "-p",
28567
28609
  String(target.port)
28568
28610
  ];
28569
- if (target.sshKey)
28570
- args.push("-i", target.sshKey);
28611
+ if (key) {
28612
+ args.push("-o", "IdentitiesOnly=yes", "-i", key);
28613
+ }
28571
28614
  return args;
28572
28615
  }
28573
28616
  async function sshExec(target, cmd, opts = {}) {
@@ -28610,28 +28653,23 @@ async function canSsh(target) {
28610
28653
  const res = await sshExec(target, "true", { quiet: true });
28611
28654
  return res.exitCode === 0;
28612
28655
  }
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
28656
  async function scpUpload(target, localPath, remotePath) {
28657
+ const key = pickSshKey(target);
28625
28658
  const args = [
28626
28659
  "-o",
28627
28660
  "BatchMode=yes",
28628
28661
  "-o",
28629
28662
  "StrictHostKeyChecking=accept-new",
28663
+ "-o",
28664
+ "PreferredAuthentications=publickey",
28665
+ "-o",
28666
+ "ConnectTimeout=5",
28630
28667
  "-P",
28631
28668
  String(target.port)
28632
28669
  ];
28633
- if (target.sshKey)
28634
- args.push("-i", target.sshKey);
28670
+ if (key) {
28671
+ args.push("-o", "IdentitiesOnly=yes", "-i", key);
28672
+ }
28635
28673
  args.push(localPath, `${target.user}@${target.host}:${remotePath}`);
28636
28674
  const proc2 = spawn3({ cmd: ["scp", ...args], stderr: "pipe" });
28637
28675
  const [stderr, exitCode] = await Promise.all([
@@ -28689,6 +28727,18 @@ JSON`);
28689
28727
  }
28690
28728
 
28691
28729
  // src/deploy/bootstrap.ts
28730
+ async function waitForAnySsh(targets, opts = {}) {
28731
+ const timeout = opts.timeoutMs ?? 300000;
28732
+ const interval = opts.intervalMs ?? 5000;
28733
+ const start = Date.now();
28734
+ while (Date.now() - start < timeout) {
28735
+ const results = await Promise.all(targets.map((t) => canSsh(t)));
28736
+ if (results.some(Boolean))
28737
+ return;
28738
+ await Bun.sleep(interval);
28739
+ }
28740
+ throw new Error(`Timed out waiting for SSH on ${targets.map((t) => `${t.user}@${t.host}`).join(" or ")}`);
28741
+ }
28692
28742
  async function bootstrap(inputs) {
28693
28743
  const { cfg, state, rootDir } = inputs;
28694
28744
  if (state.kind === "unreachable") {
@@ -28710,12 +28760,16 @@ async function bootstrap(inputs) {
28710
28760
  cfg.target.host = tfOut.serverIp;
28711
28761
  saveDeployConfig(rootDir, cfg);
28712
28762
  log2("Waiting for SSH to come up...");
28713
- await waitForSsh({ ...cfg.target, user: "root" });
28763
+ await waitForAnySsh([
28764
+ { ...cfg.target, user: "root" },
28765
+ { ...cfg.target, user: cfg.target.user }
28766
+ ]);
28714
28767
  ok("SSH reachable");
28715
28768
  }
28716
- if (state.kind === "unreachable" || state.kind === "no-docker") {
28769
+ const needAnsible = state.kind === "unreachable" || state.kind === "no-docker" || inputs.forceAnsible === true;
28770
+ if (needAnsible) {
28717
28771
  log2("Running Ansible bootstrap (Docker + firewall + SSH hardening)...");
28718
- const deployUserWorks = state.kind === "no-docker" && await canSsh(cfg.target);
28772
+ const deployUserWorks = state.kind !== "unreachable" && await canSsh(cfg.target);
28719
28773
  const asRoot = !deployUserWorks;
28720
28774
  await runAnsible({
28721
28775
  target: cfg.target,
@@ -28724,7 +28778,8 @@ async function bootstrap(inputs) {
28724
28778
  });
28725
28779
  ok("Host bootstrapped");
28726
28780
  }
28727
- if (state.kind !== "ready") {
28781
+ const needUpStack = state.kind !== "ready" || state.marker === null || state.marker.configHash !== inputs.configHash || !await isRegistryRunning(cfg);
28782
+ if (needUpStack) {
28728
28783
  await upStack(inputs);
28729
28784
  ok("Docker stack up");
28730
28785
  }
@@ -28734,9 +28789,14 @@ async function bootstrap(inputs) {
28734
28789
  updatedAt: new Date().toISOString()
28735
28790
  });
28736
28791
  }
28792
+ async function isRegistryRunning(cfg) {
28793
+ const res = await sshExec(cfg.target, `cd ${cfg.target.remoteDir} && docker compose ps --status running --format '{{.Service}}' 2>/dev/null || true`, { quiet: true });
28794
+ return res.stdout.split(`
28795
+ `).map((s) => s.trim()).includes("registry");
28796
+ }
28737
28797
  async function upStack(inputs) {
28738
28798
  const { cfg } = inputs;
28739
- const workDir = join15(tmpdir2(), "arc-deploy", `stack-${Date.now()}`);
28799
+ const workDir = join17(tmpdir2(), "arc-deploy", `stack-${Date.now()}`);
28740
28800
  mkdirSync12(workDir, { recursive: true });
28741
28801
  await assertRegistryDnsResolves(cfg);
28742
28802
  const password = process.env[cfg.registry.passwordEnv];
@@ -28744,17 +28804,17 @@ async function upStack(inputs) {
28744
28804
  throw new Error(`Registry password env var ${cfg.registry.passwordEnv} is not set. ` + `Set it (e.g. \`export ${cfg.registry.passwordEnv}=...\`) before bootstrap.`);
28745
28805
  }
28746
28806
  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 }));
28807
+ writeFileSync13(join17(workDir, "htpasswd"), htpasswdLine);
28808
+ writeFileSync13(join17(workDir, "Caddyfile"), generateCaddyfile(cfg));
28809
+ writeFileSync13(join17(workDir, "docker-compose.yml"), generateCompose({ cfg }));
28750
28810
  await assertExec(cfg.target, `sudo mkdir -p ${cfg.target.remoteDir} && sudo chown ${cfg.target.user}:${cfg.target.user} ${cfg.target.remoteDir}`);
28751
28811
  for (const name of Object.keys(cfg.envs)) {
28752
28812
  await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/${name}`);
28753
28813
  }
28754
28814
  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`);
28815
+ await scpUpload(cfg.target, join17(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
28816
+ await scpUpload(cfg.target, join17(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
28817
+ await scpUpload(cfg.target, join17(workDir, "htpasswd"), `${cfg.target.remoteDir}/registry-auth/htpasswd`);
28758
28818
  await assertExec(cfg.target, `touch ${cfg.target.remoteDir}/.env`);
28759
28819
  await assertExec(cfg.target, `cd ${cfg.target.remoteDir} && docker compose pull --ignore-pull-failures caddy registry && docker compose up -d caddy registry`);
28760
28820
  await sshDockerLogin(cfg);
@@ -28768,18 +28828,23 @@ async function sshDockerLogin(cfg) {
28768
28828
  if (!password) {
28769
28829
  throw new Error(`Registry password env var ${cfg.registry.passwordEnv} is not set on the deploy host (CLI machine).`);
28770
28830
  }
28771
- const cmd = `echo "$ARC_REGISTRY_PASSWORD_FORWARDED" | docker login ${cfg.registry.domain} -u ${cfg.registry.username} --password-stdin`;
28831
+ const cmd = `docker login ${cfg.registry.domain} -u ${cfg.registry.username} --password-stdin`;
28772
28832
  const proc2 = spawn4({
28773
28833
  cmd: [
28774
28834
  "ssh",
28775
28835
  ...baseSshArgs(cfg.target),
28776
28836
  `${cfg.target.user}@${cfg.target.host}`,
28777
28837
  "--",
28778
- `ARC_REGISTRY_PASSWORD_FORWARDED='${password.replace(/'/g, "'\\''")}' bash -c ${JSON.stringify(cmd)}`
28838
+ cmd
28779
28839
  ],
28840
+ stdin: "pipe",
28780
28841
  stdout: "pipe",
28781
28842
  stderr: "pipe"
28782
28843
  });
28844
+ if (proc2.stdin) {
28845
+ await proc2.stdin.write(new TextEncoder().encode(password));
28846
+ await proc2.stdin.end?.();
28847
+ }
28783
28848
  const exit = await proc2.exited;
28784
28849
  if (exit !== 0) {
28785
28850
  const stderr = await new Response(proc2.stderr).text();
@@ -28801,24 +28866,39 @@ async function listConfiguredEnvs(cfg) {
28801
28866
  return [...set];
28802
28867
  }
28803
28868
  async function assertRegistryDnsResolves(cfg) {
28869
+ const apex = apexDomain(cfg.registry.domain);
28870
+ let nameservers = await digQuery("8.8.8.8", "NS", apex);
28871
+ nameservers = nameservers.map((s) => s.replace(/\.$/, ""));
28872
+ const sources = [...nameservers, "1.1.1.1", "8.8.8.8"];
28873
+ let lastAnswers = [];
28874
+ for (const source of sources) {
28875
+ const answers = await digQuery(source, "A", cfg.registry.domain);
28876
+ if (answers.length === 0)
28877
+ continue;
28878
+ lastAnswers = answers;
28879
+ if (answers.includes(cfg.target.host))
28880
+ return;
28881
+ }
28882
+ if (lastAnswers.length === 0) {
28883
+ 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.`);
28884
+ }
28885
+ 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.`);
28886
+ }
28887
+ function apexDomain(host) {
28888
+ const parts = host.split(".");
28889
+ return parts.slice(-2).join(".");
28890
+ }
28891
+ async function digQuery(server, type, name) {
28804
28892
  const proc2 = spawn4({
28805
- cmd: ["dig", "+short", "+time=3", "+tries=1", cfg.registry.domain],
28893
+ cmd: ["dig", `@${server}`, "+short", "+time=3", "+tries=1", type, name],
28806
28894
  stdout: "pipe",
28807
28895
  stderr: "ignore"
28808
28896
  });
28809
28897
  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(`
28898
+ if (exit !== 0)
28899
+ return [];
28900
+ return (await new Response(proc2.stdout).text()).split(`
28815
28901
  `).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
28902
  }
28823
28903
 
28824
28904
  // src/deploy/deploy-env.ts
@@ -28831,7 +28911,7 @@ async function updateEnvDeployment(opts) {
28831
28911
  const envPath = `${cfg.target.remoteDir}/.env`;
28832
28912
  const escapedRef = fullRef.replace(/"/g, "\\\"");
28833
28913
  const updateScript = [
28834
- `touch ${envPath}`,
28914
+ `touch ${envPath} && `,
28835
28915
  `awk -v line="${envVarName}=${escapedRef}" -v key="${envVarName}=" '`,
28836
28916
  ` BEGIN { replaced=0 } `,
28837
28917
  ` $0 ~ "^"key { print line; replaced=1; next } `,
@@ -28881,15 +28961,15 @@ var {spawn: spawn5 } = globalThis.Bun;
28881
28961
  import { createHash as createHash3 } from "crypto";
28882
28962
  import {
28883
28963
  copyFileSync as copyFileSync2,
28884
- existsSync as existsSync13,
28964
+ existsSync as existsSync16,
28885
28965
  mkdirSync as mkdirSync13,
28886
- readFileSync as readFileSync12,
28887
- realpathSync as realpathSync3,
28966
+ readFileSync as readFileSync13,
28967
+ realpathSync as realpathSync2,
28888
28968
  writeFileSync as writeFileSync14
28889
28969
  } from "fs";
28890
28970
  import { tmpdir as tmpdir3 } from "os";
28891
- import { dirname as dirname8, join as join16 } from "path";
28892
- import { fileURLToPath as fileURLToPath6 } from "url";
28971
+ import { dirname as dirname7, join as join18 } from "path";
28972
+ import { fileURLToPath as fileURLToPath5 } from "url";
28893
28973
 
28894
28974
  // src/deploy/image-template.ts
28895
28975
  function generateDockerfile(inputs) {
@@ -28938,8 +29018,8 @@ function generateDockerfile(inputs) {
28938
29018
  // src/deploy/image.ts
28939
29019
  async function buildImage(ws, opts) {
28940
29020
  await ensureDocker();
28941
- const manifestPath = join16(ws.modulesDir, "manifest.json");
28942
- if (!existsSync13(manifestPath)) {
29021
+ const manifestPath = join18(ws.arcDir, "manifest.json");
29022
+ if (!existsSync16(manifestPath)) {
28943
29023
  throw new Error(`No build manifest at ${manifestPath}. Run \`arc platform build\` first or omit --skip-build.`);
28944
29024
  }
28945
29025
  embedCliBundle(ws);
@@ -28949,12 +29029,13 @@ async function buildImage(ws, opts) {
28949
29029
  const dockerfileInputs = collectDockerfileInputs(ws);
28950
29030
  const dockerfile = generateDockerfile(dockerfileInputs);
28951
29031
  const buildContextDir = ws.rootDir;
28952
- const dockerfileDir = join16(tmpdir3(), `arc-image-${Date.now()}`);
29032
+ const dockerfileDir = join18(tmpdir3(), `arc-image-${Date.now()}`);
28953
29033
  mkdirSync13(dockerfileDir, { recursive: true });
28954
- const dockerfilePath = join16(dockerfileDir, "Dockerfile");
29034
+ const dockerfilePath = join18(dockerfileDir, "Dockerfile");
28955
29035
  writeFileSync14(dockerfilePath, dockerfile);
28956
29036
  const buildArgs = [
28957
29037
  "build",
29038
+ "--platform=linux/amd64",
28958
29039
  "-f",
28959
29040
  dockerfilePath,
28960
29041
  "-t",
@@ -28976,20 +29057,20 @@ async function buildImage(ws, opts) {
28976
29057
  }
28977
29058
  function embedCliBundle(ws) {
28978
29059
  const source = locateCliBundle();
28979
- const target = join16(ws.arcDir, "host.js");
29060
+ const target = join18(ws.arcDir, "host.js");
28980
29061
  copyFileSync2(source, target);
28981
29062
  }
28982
29063
  function locateCliBundle() {
28983
- const here = fileURLToPath6(import.meta.url);
28984
- let cur = dirname8(here);
29064
+ const here = fileURLToPath5(import.meta.url);
29065
+ let cur = dirname7(here);
28985
29066
  while (cur !== "/" && cur !== "") {
28986
- const candidate = join16(cur, "package.json");
28987
- if (existsSync13(candidate)) {
29067
+ const candidate = join18(cur, "package.json");
29068
+ if (existsSync16(candidate)) {
28988
29069
  try {
28989
- const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
29070
+ const pkg = JSON.parse(readFileSync13(candidate, "utf-8"));
28990
29071
  if (pkg.name === "@arcote.tech/arc-cli") {
28991
- const distIndex = join16(realpathSync3(cur), "dist", "index.js");
28992
- if (!existsSync13(distIndex)) {
29072
+ const distIndex = join18(realpathSync2(cur), "dist", "index.js");
29073
+ if (!existsSync16(distIndex)) {
28993
29074
  throw new Error(`arc-cli bundle missing at ${distIndex}. Run \`bun run build\` in packages/cli/.`);
28994
29075
  }
28995
29076
  return distIndex;
@@ -28999,7 +29080,7 @@ function locateCliBundle() {
28999
29080
  throw e;
29000
29081
  }
29001
29082
  }
29002
- const parent = dirname8(cur);
29083
+ const parent = dirname7(cur);
29003
29084
  if (parent === cur)
29004
29085
  break;
29005
29086
  cur = parent;
@@ -29021,17 +29102,17 @@ async function ensureDocker() {
29021
29102
  }
29022
29103
  }
29023
29104
  function computeContentHash(manifestPath) {
29024
- const raw = JSON.parse(readFileSync12(manifestPath, "utf-8"));
29105
+ const raw = JSON.parse(readFileSync13(manifestPath, "utf-8"));
29025
29106
  delete raw.buildTime;
29026
29107
  const canonical = JSON.stringify(raw);
29027
29108
  return createHash3("sha256").update(canonical).digest("hex").slice(0, 12);
29028
29109
  }
29029
29110
  function collectDockerfileInputs(ws) {
29030
- const hasPublicDir = existsSync13(join16(ws.rootDir, "public"));
29031
- const hasLocales = existsSync13(join16(ws.rootDir, "locales"));
29111
+ const hasPublicDir = existsSync16(join18(ws.rootDir, "public"));
29112
+ const hasLocales = existsSync16(join18(ws.rootDir, "locales"));
29032
29113
  let manifestPath;
29033
29114
  for (const name of ["manifest.webmanifest", "manifest.json"]) {
29034
- if (existsSync13(join16(ws.rootDir, name))) {
29115
+ if (existsSync16(join18(ws.rootDir, name))) {
29035
29116
  manifestPath = name;
29036
29117
  break;
29037
29118
  }
@@ -29852,14 +29933,14 @@ async function platformDeploy(envArg, options = {}) {
29852
29933
  err(`Unknown env "${envArg}". Known: ${Object.keys(cfg.envs).join(", ")}`);
29853
29934
  process.exit(1);
29854
29935
  })() : Object.keys(cfg.envs);
29855
- const manifestPath = join17(ws.modulesDir, "manifest.json");
29936
+ const manifestPath = join19(ws.arcDir, "manifest.json");
29856
29937
  if (!options.imageTag) {
29857
- const needBuild = options.rebuild || !existsSync14(manifestPath);
29938
+ const needBuild = options.rebuild || !existsSync17(manifestPath);
29858
29939
  if (needBuild && !options.skipBuild) {
29859
29940
  log2("Building platform...");
29860
29941
  await buildAll(ws, { noCache: options.rebuild });
29861
29942
  ok("Build complete");
29862
- } else if (!existsSync14(manifestPath)) {
29943
+ } else if (!existsSync17(manifestPath)) {
29863
29944
  err("No build found and --skip-build was set.");
29864
29945
  process.exit(1);
29865
29946
  }
@@ -29884,25 +29965,26 @@ async function platformDeploy(envArg, options = {}) {
29884
29965
  log2(`contentHash: ${contentHash}`);
29885
29966
  return;
29886
29967
  }
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
29968
  }
29893
29969
  log2("Inspecting remote server...");
29894
29970
  const state = await detectRemoteState(cfg);
29895
29971
  log2(`Remote state: ${state.kind}`);
29896
29972
  const cliVersion = readCliVersion();
29897
29973
  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
- });
29974
+ await bootstrap({
29975
+ cfg,
29976
+ rootDir: ws.rootDir,
29977
+ state,
29978
+ cliVersion,
29979
+ configHash,
29980
+ forceAnsible: options.forceBootstrap
29981
+ });
29982
+ if (!options.imageTag) {
29983
+ log2(`Logging in to ${cfg.registry.domain}...`);
29984
+ await dockerLogin(cfg.registry);
29985
+ log2(`Pushing ${fullRef}...`);
29986
+ await dockerPush(fullRef);
29987
+ ok("Image pushed");
29906
29988
  }
29907
29989
  for (const env2 of targetEnvs) {
29908
29990
  log2(`Updating env "${env2}"...`);
@@ -29921,17 +30003,17 @@ async function platformDeploy(envArg, options = {}) {
29921
30003
  }
29922
30004
  function readCliVersion() {
29923
30005
  try {
29924
- let cur = dirname9(fileURLToPath7(import.meta.url));
29925
- const root = dirname9(cur).startsWith("/") ? "/" : ".";
30006
+ let cur = dirname8(fileURLToPath6(import.meta.url));
30007
+ const root = dirname8(cur).startsWith("/") ? "/" : ".";
29926
30008
  while (cur !== root && cur !== "") {
29927
- const candidate = join17(cur, "package.json");
29928
- if (existsSync14(candidate)) {
29929
- const pkg = JSON.parse(readFileSync13(candidate, "utf-8"));
30009
+ const candidate = join19(cur, "package.json");
30010
+ if (existsSync17(candidate)) {
30011
+ const pkg = JSON.parse(readFileSync14(candidate, "utf-8"));
29930
30012
  if (pkg.name === "@arcote.tech/arc-cli") {
29931
30013
  return pkg.version ?? "unknown";
29932
30014
  }
29933
30015
  }
29934
- const parent = dirname9(cur);
30016
+ const parent = dirname8(cur);
29935
30017
  if (parent === cur)
29936
30018
  break;
29937
30019
  cur = parent;
@@ -29942,16 +30024,16 @@ function readCliVersion() {
29942
30024
  }
29943
30025
  }
29944
30026
  async function hashDeployConfig(rootDir) {
29945
- const p2 = join17(rootDir, "deploy.arc.json");
29946
- const content = readFileSync13(p2);
30027
+ const p2 = join19(rootDir, "deploy.arc.json");
30028
+ const content = readFileSync14(p2);
29947
30029
  const hasher = new Bun.CryptoHasher("sha256");
29948
30030
  hasher.update(content);
29949
30031
  return hasher.digest("hex").slice(0, 16);
29950
30032
  }
29951
30033
 
29952
30034
  // src/platform/startup.ts
29953
- import { existsSync as existsSync16, readFileSync as readFileSync14, watch } from "fs";
29954
- import { join as join19 } from "path";
30035
+ import { existsSync as existsSync19, readFileSync as readFileSync15, watch } from "fs";
30036
+ import { join as join21 } from "path";
29955
30037
 
29956
30038
  // ../host/src/create-server.ts
29957
30039
  var import_jsonwebtoken = __toESM(require_jsonwebtoken(), 1);
@@ -31197,8 +31279,8 @@ function querySubscriptionHandler() {
31197
31279
  subscriptionId,
31198
31280
  data: data ?? null
31199
31281
  });
31200
- } catch (err2) {
31201
- console.error(`[Arc] Query subscription error:`, err2);
31282
+ } catch (err3) {
31283
+ console.error(`[Arc] Query subscription error:`, err3);
31202
31284
  }
31203
31285
  };
31204
31286
  sendData();
@@ -31401,25 +31483,14 @@ async function createArcServer(config) {
31401
31483
  };
31402
31484
  }
31403
31485
  // 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
- }
31486
+ init_i18n();
31487
+ import { existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
31488
+ import { join as join20 } from "path";
31489
+ function generateShellHtml(appName, manifest, initial) {
31490
+ const initialUrl = initial ? `/browser/${initial.file}` : null;
31491
+ if (!initialUrl) {
31492
+ throw new Error("generateShellHtml: initial bundle missing from manifest");
31412
31493
  }
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
31494
  return `<!doctype html>
31424
31495
  <html lang="en">
31425
31496
  <head>
@@ -31430,18 +31501,13 @@ function generateShellHtml(appName, manifest, arcEntries) {
31430
31501
  <link rel="manifest" href="/manifest.json">` : ""}
31431
31502
  <link rel="stylesheet" href="/styles.css" />
31432
31503
  <link rel="stylesheet" href="/theme.css" />
31433
- <script type="importmap">${JSON.stringify(importMap)}</script>
31504
+ <link rel="modulepreload" href="${initialUrl}" />
31434
31505
  </head>
31435
31506
  <body>
31436
31507
  <div id="root"></div>
31437
31508
  <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
- );
31509
+ import { startApp } from "${initialUrl}";
31510
+ startApp("root");
31445
31511
  </script>
31446
31512
  </body>
31447
31513
  </html>`;
@@ -31466,28 +31532,39 @@ function getMime(path4) {
31466
31532
  return MIME[ext2] ?? "application/octet-stream";
31467
31533
  }
31468
31534
  function serveFile(filePath, headers = {}) {
31469
- if (!existsSync15(filePath))
31535
+ if (!existsSync18(filePath))
31470
31536
  return new Response("Not Found", { status: 404 });
31471
31537
  return new Response(Bun.file(filePath), {
31472
31538
  headers: { "Content-Type": getMime(filePath), ...headers }
31473
31539
  });
31474
31540
  }
31475
- var MODULE_SIG_SECRET = process.env.ARC_MODULE_SECRET ?? crypto.randomUUID();
31541
+ var MODULE_SIG_SECRET = process.env.ARC_MODULE_SECRET ?? "";
31476
31542
  var MODULE_SIG_TTL = 3600;
31477
- function signChunkUrl(chunk, file) {
31543
+ function ensureModuleSigSecret(ws, devMode) {
31544
+ if (MODULE_SIG_SECRET)
31545
+ return;
31546
+ if (devMode) {
31547
+ const hasher = new Bun.CryptoHasher("sha256");
31548
+ hasher.update(`arc-dev-secret:${ws.rootDir}`);
31549
+ MODULE_SIG_SECRET = hasher.digest("hex").slice(0, 32);
31550
+ } else {
31551
+ MODULE_SIG_SECRET = crypto.randomUUID();
31552
+ }
31553
+ }
31554
+ function signGroupUrl(file) {
31478
31555
  const exp = Math.floor(Date.now() / 1000) + MODULE_SIG_TTL;
31479
31556
  const hasher = new Bun.CryptoHasher("sha256");
31480
- hasher.update(`${chunk}/${file}:${exp}:${MODULE_SIG_SECRET}`);
31557
+ hasher.update(`${file}:${exp}:${MODULE_SIG_SECRET}`);
31481
31558
  const sig = hasher.digest("hex").slice(0, 16);
31482
- return `/modules/${chunk}/${file}?sig=${sig}&exp=${exp}`;
31559
+ return `/browser/${file}?sig=${sig}&exp=${exp}`;
31483
31560
  }
31484
- function verifyChunkSignature(chunk, file, sig, exp) {
31561
+ function verifyGroupSignature(file, sig, exp) {
31485
31562
  if (!sig || !exp)
31486
31563
  return false;
31487
31564
  if (Number(exp) < Date.now() / 1000)
31488
31565
  return false;
31489
31566
  const hasher = new Bun.CryptoHasher("sha256");
31490
- hasher.update(`${chunk}/${file}:${exp}:${MODULE_SIG_SECRET}`);
31567
+ hasher.update(`${file}:${exp}:${MODULE_SIG_SECRET}`);
31491
31568
  return hasher.digest("hex").slice(0, 16) === sig;
31492
31569
  }
31493
31570
  function decodeTokenPayload(jwt2) {
@@ -31522,90 +31599,85 @@ function parseArcTokensHeader(header) {
31522
31599
  return payloads;
31523
31600
  }
31524
31601
  async function filterManifestForTokens(manifest, moduleAccessMap, tokenPayloads) {
31525
- const allowedChunks = new Set(["public"]);
31602
+ const allowedGroups = new Set;
31526
31603
  for (const t of tokenPayloads) {
31527
31604
  if (t?.tokenType)
31528
- allowedChunks.add(t.tokenType);
31605
+ allowedGroups.add(t.tokenType);
31529
31606
  }
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);
31607
+ const filteredGroups = {};
31608
+ for (const [name, group] of Object.entries(manifest.groups)) {
31609
+ if (!allowedGroups.has(name))
31536
31610
  continue;
31537
- }
31538
- const access = moduleAccessMap.get(mod.name);
31539
- let granted = true;
31540
- if (access && access.rules.length > 0) {
31541
- granted = false;
31611
+ let allGranted = true;
31612
+ for (const moduleName of group.modules) {
31613
+ const access = moduleAccessMap.get(moduleName);
31614
+ if (!access || access.rules.length === 0)
31615
+ continue;
31616
+ let granted = false;
31542
31617
  for (const rule of access.rules) {
31543
- if (rule.token.name !== mod.chunk)
31618
+ if (rule.token.name !== name)
31544
31619
  continue;
31545
- const matching = tokenPayloads.find((t) => t.tokenType === rule.token.name);
31620
+ const matching = tokenPayloads.find((t) => t.tokenType === name);
31546
31621
  if (!matching)
31547
31622
  continue;
31548
31623
  granted = rule.check ? await rule.check(matching) : true;
31549
31624
  if (granted)
31550
31625
  break;
31551
31626
  }
31627
+ if (!granted) {
31628
+ allGranted = false;
31629
+ break;
31630
+ }
31552
31631
  }
31553
- if (granted) {
31554
- filtered.push({ ...mod, url: signChunkUrl(mod.chunk, mod.file) });
31632
+ if (allGranted) {
31633
+ filteredGroups[name] = { ...group, url: signGroupUrl(group.file) };
31555
31634
  }
31556
31635
  }
31557
31636
  return {
31558
- modules: filtered,
31559
- chunks: manifest.chunks,
31560
- shellHash: manifest.shellHash,
31637
+ initial: manifest.initial,
31638
+ groups: filteredGroups,
31639
+ sharedChunks: manifest.sharedChunks,
31561
31640
  stylesHash: manifest.stylesHash,
31562
31641
  buildTime: manifest.buildTime
31563
31642
  };
31564
31643
  }
31565
- var CHUNK_NAME_RE = /^[A-Za-z0-9_-]+$/;
31566
- var MODULE_FILE_RE = /^[A-Za-z0-9_.-]+\.js$/;
31567
- function staticFilesHandler(ws, devMode) {
31644
+ var BROWSER_FILE_RE = /^[A-Za-z0-9_.-]+\.js$/;
31645
+ function staticFilesHandler(ws, devMode, getManifest) {
31568
31646
  return (_req, url, ctx) => {
31569
31647
  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)) {
31648
+ if (path4.startsWith("/browser/")) {
31649
+ const file = path4.slice(9);
31650
+ if (!BROWSER_FILE_RE.test(file)) {
31581
31651
  return new Response("Not Found", { status: 404, headers: ctx.corsHeaders });
31582
31652
  }
31583
- if (chunk !== "public") {
31653
+ const manifest = getManifest();
31654
+ const isGroupEntry = Object.values(manifest.groups).some((g3) => g3.file === file);
31655
+ if (isGroupEntry) {
31584
31656
  const sig = url.searchParams.get("sig");
31585
31657
  const exp = url.searchParams.get("exp");
31586
- if (!verifyChunkSignature(chunk, file, sig, exp)) {
31658
+ if (!verifyGroupSignature(file, sig, exp)) {
31587
31659
  return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
31588
31660
  }
31589
31661
  }
31590
- return serveFile(join18(ws.modulesDir, chunk, file), {
31662
+ return serveFile(join20(ws.browserDir, file), {
31591
31663
  ...ctx.corsHeaders,
31592
31664
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
31593
31665
  });
31594
31666
  }
31595
31667
  if (path4.startsWith("/locales/"))
31596
- return serveFile(join18(ws.arcDir, path4.slice(1)), ctx.corsHeaders);
31668
+ return serveFile(join20(ws.arcDir, path4.slice(1)), ctx.corsHeaders);
31597
31669
  if (path4.startsWith("/assets/"))
31598
- return serveFile(join18(ws.assetsDir, path4.slice(8)), ctx.corsHeaders);
31670
+ return serveFile(join20(ws.assetsDir, path4.slice(8)), ctx.corsHeaders);
31599
31671
  if (path4 === "/styles.css")
31600
- return serveFile(join18(ws.arcDir, "styles.css"), ctx.corsHeaders);
31672
+ return serveFile(join20(ws.arcDir, "styles.css"), ctx.corsHeaders);
31601
31673
  if (path4 === "/theme.css")
31602
- return serveFile(join18(ws.arcDir, "theme.css"), ctx.corsHeaders);
31674
+ return serveFile(join20(ws.arcDir, "theme.css"), ctx.corsHeaders);
31603
31675
  if ((path4 === "/manifest.json" || path4 === "/manifest.webmanifest") && ws.manifest) {
31604
31676
  return serveFile(ws.manifest.path, ctx.corsHeaders);
31605
31677
  }
31606
31678
  if (path4.lastIndexOf(".") > path4.lastIndexOf("/")) {
31607
- const publicFile = join18(ws.publicDir, path4.slice(1));
31608
- if (existsSync15(publicFile))
31679
+ const publicFile = join20(ws.publicDir, path4.slice(1));
31680
+ if (existsSync18(publicFile))
31609
31681
  return serveFile(publicFile, ctx.corsHeaders);
31610
31682
  }
31611
31683
  return null;
@@ -31630,7 +31702,7 @@ function apiEndpointsHandler(ws, getManifest, cm, moduleAccessMap) {
31630
31702
  if (url.pathname === "/health") {
31631
31703
  return Response.json({
31632
31704
  status: "ok",
31633
- modules: getManifest().modules.length,
31705
+ groups: Object.keys(getManifest().groups).length,
31634
31706
  clients: cm?.clientCount ?? 0
31635
31707
  }, { headers: ctx.corsHeaders });
31636
31708
  }
@@ -31662,22 +31734,23 @@ function devReloadHandler(sseClients) {
31662
31734
  });
31663
31735
  };
31664
31736
  }
31665
- function spaFallbackHandler(shellHtml) {
31737
+ function spaFallbackHandler(getShellHtml) {
31666
31738
  return (_req, _url, ctx) => {
31667
- return new Response(shellHtml, {
31739
+ return new Response(getShellHtml(), {
31668
31740
  headers: { ...ctx.corsHeaders, "Content-Type": "text/html" }
31669
31741
  });
31670
31742
  };
31671
31743
  }
31672
31744
  async function startPlatformServer(opts) {
31673
31745
  const { ws, port, devMode, context } = opts;
31746
+ ensureModuleSigSecret(ws, !!devMode);
31674
31747
  const moduleAccessMap = opts.moduleAccess ?? new Map;
31675
31748
  let manifest = opts.manifest;
31676
31749
  const getManifest = () => manifest;
31677
31750
  const setManifest = (m4) => {
31678
31751
  manifest = m4;
31679
31752
  };
31680
- const shellHtml = generateShellHtml(ws.appName, ws.manifest, opts.arcEntries);
31753
+ const getShellHtml = () => generateShellHtml(ws.appName, ws.manifest, manifest?.initial);
31681
31754
  const sseClients = new Set;
31682
31755
  const notifyReload = (m4) => {
31683
31756
  const data = JSON.stringify(m4);
@@ -31714,8 +31787,8 @@ async function startPlatformServer(opts) {
31714
31787
  const handlers = [
31715
31788
  apiEndpointsHandler(ws, getManifest, null, moduleAccessMap),
31716
31789
  devReloadHandler(sseClients),
31717
- staticFilesHandler(ws, !!devMode),
31718
- spaFallbackHandler(shellHtml)
31790
+ staticFilesHandler(ws, !!devMode, getManifest),
31791
+ spaFallbackHandler(getShellHtml)
31719
31792
  ];
31720
31793
  for (const handler of handlers) {
31721
31794
  const response = await handler(req, url, ctx);
@@ -31735,7 +31808,7 @@ async function startPlatformServer(opts) {
31735
31808
  };
31736
31809
  }
31737
31810
  const { createBunSQLiteAdapterFactory: createBunSQLiteAdapterFactory2 } = await Promise.resolve().then(() => (init_dist(), exports_dist2));
31738
- const dbPath = opts.dbPath || join18(ws.arcDir, "data", "arc.db");
31811
+ const dbPath = opts.dbPath || join20(ws.arcDir, "data", "arc.db");
31739
31812
  const dbDir = dbPath.substring(0, dbPath.lastIndexOf("/"));
31740
31813
  if (dbDir)
31741
31814
  mkdirSync14(dbDir, { recursive: true });
@@ -31746,8 +31819,8 @@ async function startPlatformServer(opts) {
31746
31819
  httpHandlers: [
31747
31820
  apiEndpointsHandler(ws, getManifest, null, moduleAccessMap),
31748
31821
  devReloadHandler(sseClients),
31749
- staticFilesHandler(ws, !!devMode),
31750
- spaFallbackHandler(shellHtml)
31822
+ staticFilesHandler(ws, !!devMode, getManifest),
31823
+ spaFallbackHandler(getShellHtml)
31751
31824
  ],
31752
31825
  onWsClose: (clientId) => cleanupClientSubs(clientId)
31753
31826
  });
@@ -31765,17 +31838,17 @@ async function startPlatformServer(opts) {
31765
31838
  async function startPlatform(opts) {
31766
31839
  const { ws, devMode } = opts;
31767
31840
  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");
31841
+ const dbPath = opts.dbPath ?? join21(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
31769
31842
  let manifest;
31770
31843
  if (devMode) {
31771
31844
  manifest = await buildAll(ws);
31772
31845
  } else {
31773
- const manifestPath = join19(ws.modulesDir, "manifest.json");
31774
- if (!existsSync16(manifestPath)) {
31846
+ const manifestPath = join21(ws.arcDir, "manifest.json");
31847
+ if (!existsSync19(manifestPath)) {
31775
31848
  err("No build found. Run `arc platform build` first.");
31776
31849
  process.exit(1);
31777
31850
  }
31778
- manifest = JSON.parse(readFileSync14(manifestPath, "utf-8"));
31851
+ manifest = JSON.parse(readFileSync15(manifestPath, "utf-8"));
31779
31852
  }
31780
31853
  log2("Loading server context...");
31781
31854
  const { context, moduleAccess } = await loadServerContext(ws);
@@ -31784,7 +31857,6 @@ async function startPlatform(opts) {
31784
31857
  } else {
31785
31858
  log2("No context \u2014 server endpoints skipped");
31786
31859
  }
31787
- const arcEntries = collectArcPeerDeps(ws.packages);
31788
31860
  const platform3 = await startPlatformServer({
31789
31861
  ws,
31790
31862
  port,
@@ -31792,8 +31864,7 @@ async function startPlatform(opts) {
31792
31864
  context,
31793
31865
  moduleAccess,
31794
31866
  dbPath,
31795
- devMode,
31796
- arcEntries
31867
+ devMode
31797
31868
  });
31798
31869
  ok(`Server on http://localhost:${port}`);
31799
31870
  if (platform3.contextHandler) {
@@ -31825,7 +31896,7 @@ function attachDevWatcher(ws, platform3) {
31825
31896
  const next = await buildAll(ws);
31826
31897
  platform3.setManifest(next);
31827
31898
  platform3.notifyReload(next);
31828
- ok(`Rebuilt \u2014 ${next.modules.length} module(s)`);
31899
+ ok(`Rebuilt \u2014 initial + ${Object.keys(next.groups).length} group(s)`);
31829
31900
  } catch (e2) {
31830
31901
  console.error(`Rebuild failed: ${e2}`);
31831
31902
  } finally {
@@ -31834,8 +31905,8 @@ function attachDevWatcher(ws, platform3) {
31834
31905
  }, 300);
31835
31906
  };
31836
31907
  for (const pkg of ws.packages) {
31837
- const srcDir = join19(pkg.path, "src");
31838
- if (!existsSync16(srcDir))
31908
+ const srcDir = join21(pkg.path, "src");
31909
+ if (!existsSync19(srcDir))
31839
31910
  continue;
31840
31911
  watch(srcDir, { recursive: true }, (_event, filename) => {
31841
31912
  if (!filename || filename.includes(".arc") || filename.endsWith(".d.ts") || filename.includes("node_modules") || filename.includes("dist"))
@@ -31845,8 +31916,8 @@ function attachDevWatcher(ws, platform3) {
31845
31916
  triggerRebuild();
31846
31917
  });
31847
31918
  }
31848
- const localesDir = join19(ws.rootDir, "locales");
31849
- if (existsSync16(localesDir)) {
31919
+ const localesDir = join21(ws.rootDir, "locales");
31920
+ if (existsSync19(localesDir)) {
31850
31921
  watch(localesDir, { recursive: false }, (_event, filename) => {
31851
31922
  if (!filename?.endsWith(".po"))
31852
31923
  return;
@@ -31876,7 +31947,7 @@ var platform3 = program2.command("platform").description("Platform commands \u20
31876
31947
  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
31948
  platform3.command("build").description("Build platform for production").option("--no-cache", "Force full rebuild").action((opts) => platformBuild({ noCache: opts.cache === false }));
31878
31949
  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));
31950
+ 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
31951
  program2.parse(process.argv);
31881
31952
  if (process.argv.length === 2) {
31882
31953
  program2.help();