tina4-nodejs 3.13.100 → 3.13.103

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.
@@ -2622,6 +2622,7 @@ __export(engine_exports, {
2622
2622
  Frond: () => Frond,
2623
2623
  MEMO_CACHE_MAX: () => MEMO_CACHE_MAX,
2624
2624
  TEMPLATE_CACHE_MAX: () => TEMPLATE_CACHE_MAX,
2625
+ expressionFormCache: () => expressionFormCache,
2625
2626
  filterChainCache: () => filterChainCache,
2626
2627
  pathParseCache: () => pathParseCache,
2627
2628
  setFormTokenSessionId: () => setFormTokenSessionId
@@ -3030,62 +3031,58 @@ function splitOutsideQuotes(expr, sep6) {
3030
3031
  parts.push(expr.slice(currentStart));
3031
3032
  return parts;
3032
3033
  }
3033
- function evalExpr(expr, context) {
3034
- expr = expr.trim();
3035
- if (expr.length >= 2) {
3036
- const q = expr[0];
3037
- if ((q === '"' || q === "'") && expr.endsWith(q) && !expr.slice(1, -1).includes(q)) {
3038
- return expr.slice(1, -1);
3039
- }
3040
- }
3041
- if (expr.length >= 2 && expr[0] === "(" && expr.endsWith(")")) {
3042
- let depth = 0;
3043
- let matched = true;
3044
- for (let pi = 0; pi < expr.length; pi++) {
3045
- if (expr[pi] === "(") depth++;
3046
- else if (expr[pi] === ")") depth--;
3047
- if (depth === 0 && pi < expr.length - 1) {
3048
- matched = false;
3049
- break;
3050
- }
3051
- }
3052
- if (matched) {
3053
- return evalExpr(expr.slice(1, -1), context);
3054
- }
3034
+ function parenthesizedInner(expr) {
3035
+ if (expr.length < 2 || expr[0] !== "(" || !expr.endsWith(")")) return null;
3036
+ let depth = 0;
3037
+ for (let index = 0; index < expr.length; index++) {
3038
+ if (expr[index] === "(") depth++;
3039
+ else if (expr[index] === ")") depth--;
3040
+ if (depth === 0 && index < expr.length - 1) return null;
3055
3041
  }
3056
- const ternaryIdx = findTernary(expr);
3057
- if (ternaryIdx !== -1) {
3058
- const condPart = expr.slice(0, ternaryIdx).trim();
3059
- const rest = expr.slice(ternaryIdx + 1);
3060
- const colonIdx = findColon(rest);
3061
- if (colonIdx !== -1) {
3062
- const truePart = rest.slice(0, colonIdx).trim();
3063
- const falsePart = rest.slice(colonIdx + 1).trim();
3064
- const cond = evalExpr(condPart, context);
3065
- return cond ? evalExpr(truePart, context) : evalExpr(falsePart, context);
3066
- }
3042
+ return expr.slice(1, -1);
3043
+ }
3044
+ function evalPrimary(expr, context) {
3045
+ const quote = expr[0];
3046
+ if (expr.length >= 2 && (quote === '"' || quote === "'") && expr.endsWith(quote) && !expr.slice(1, -1).includes(quote)) {
3047
+ return expr.slice(1, -1);
3067
3048
  }
3049
+ const inner = parenthesizedInner(expr);
3050
+ if (inner !== null) return evalExpr(inner, context);
3051
+ return EXPR_NOT_MATCHED;
3052
+ }
3053
+ function evalTernaryExpression(expr, context) {
3054
+ const ternaryIdx = findTernary(expr);
3055
+ if (ternaryIdx === -1) return EXPR_NOT_MATCHED;
3056
+ const rest = expr.slice(ternaryIdx + 1);
3057
+ const colonIdx = findColon(rest);
3058
+ if (colonIdx === -1) return EXPR_NOT_MATCHED;
3059
+ const condition = evalExpr(expr.slice(0, ternaryIdx).trim(), context);
3060
+ const branch = condition ? rest.slice(0, colonIdx) : rest.slice(colonIdx + 1);
3061
+ return evalExpr(branch.trim(), context);
3062
+ }
3063
+ function evalInlineIfExpression(expr, context) {
3068
3064
  const ifIdx = findOutsideQuotes(expr, " if ");
3069
- if (ifIdx >= 0) {
3070
- const elseIdx = findOutsideQuotes(expr, " else ");
3071
- if (elseIdx >= 0 && elseIdx > ifIdx) {
3072
- const valuePart = expr.slice(0, ifIdx).trim();
3073
- const condPart = expr.slice(ifIdx + 4, elseIdx).trim();
3074
- const elsePart = expr.slice(elseIdx + 6).trim();
3075
- const cond = evalExpr(condPart, context);
3076
- return cond ? evalExpr(valuePart, context) : evalExpr(elsePart, context);
3077
- }
3078
- }
3065
+ if (ifIdx < 0) return EXPR_NOT_MATCHED;
3066
+ const elseIdx = findOutsideQuotes(expr, " else ");
3067
+ if (elseIdx < 0 || elseIdx <= ifIdx) return EXPR_NOT_MATCHED;
3068
+ const condition = evalExpr(expr.slice(ifIdx + 4, elseIdx).trim(), context);
3069
+ const branch = condition ? expr.slice(0, ifIdx) : expr.slice(elseIdx + 6);
3070
+ return evalExpr(branch.trim(), context);
3071
+ }
3072
+ function evalCoalesceExpression(expr, context) {
3079
3073
  const qqIdx = findOutsideQuotes(expr, "??");
3080
- if (qqIdx !== -1) {
3081
- const left = expr.slice(0, qqIdx).trim();
3082
- const right = expr.slice(qqIdx + 2).trim();
3083
- const val = evalExpr(left, context);
3084
- if (val === null || val === void 0) {
3085
- return evalExpr(right, context);
3086
- }
3087
- return val;
3074
+ if (qqIdx === -1) return EXPR_NOT_MATCHED;
3075
+ const value = evalExpr(expr.slice(0, qqIdx).trim(), context);
3076
+ return value === null || value === void 0 ? evalExpr(expr.slice(qqIdx + 2).trim(), context) : value;
3077
+ }
3078
+ function evalConditional(expr, context) {
3079
+ for (const evaluator of [evalTernaryExpression, evalInlineIfExpression, evalCoalesceExpression]) {
3080
+ const result = evaluator(expr, context);
3081
+ if (result !== EXPR_NOT_MATCHED) return result;
3088
3082
  }
3083
+ return EXPR_NOT_MATCHED;
3084
+ }
3085
+ function evalConcatOrComparison(expr, context) {
3089
3086
  if (findOutsideQuotes(expr, "~") >= 0) {
3090
3087
  const parts = splitOutsideQuotes(expr, "~");
3091
3088
  if (parts.length > 1) {
@@ -3103,6 +3100,9 @@ function evalExpr(expr, context) {
3103
3100
  return evalComparison(expr, context);
3104
3101
  }
3105
3102
  }
3103
+ return EXPR_NOT_MATCHED;
3104
+ }
3105
+ function evalArithmeticExpression(expr, context) {
3106
3106
  for (const op of [" + ", " - ", " * ", " // ", " / ", " % ", " ** "]) {
3107
3107
  const pos = findOutsideQuotes(expr, op);
3108
3108
  if (pos >= 0) {
@@ -3115,40 +3115,15 @@ function evalExpr(expr, context) {
3115
3115
  let rNum = rVal != null ? Number(rVal) : 0;
3116
3116
  if (isNaN(lNum)) lNum = 0;
3117
3117
  if (isNaN(rNum)) rNum = 0;
3118
- const opS = op.trim();
3119
- const bothInt = Number.isInteger(lNum) && Number.isInteger(rNum) && opS !== "/";
3120
- let result;
3121
- switch (opS) {
3122
- case "+":
3123
- result = lNum + rNum;
3124
- break;
3125
- case "-":
3126
- result = lNum - rNum;
3127
- break;
3128
- case "*":
3129
- result = lNum * rNum;
3130
- break;
3131
- case "//":
3132
- result = rNum !== 0 ? Math.floor(lNum / rNum) : 0;
3133
- break;
3134
- case "/":
3135
- result = rNum !== 0 ? lNum / rNum : 0;
3136
- break;
3137
- case "%":
3138
- result = rNum !== 0 ? lNum % rNum : 0;
3139
- break;
3140
- case "**":
3141
- result = lNum ** rNum;
3142
- break;
3143
- default:
3144
- result = 0;
3145
- }
3146
- return bothInt && Number.isInteger(result) ? result : result;
3118
+ return ARITHMETIC_OPERATIONS[op.trim()](lNum, rNum);
3147
3119
  } catch {
3148
3120
  return null;
3149
3121
  }
3150
3122
  }
3151
3123
  }
3124
+ return EXPR_NOT_MATCHED;
3125
+ }
3126
+ function evalFilterExpression(expr, context) {
3152
3127
  if (findOutsideQuotes(expr, "|") >= 0) {
3153
3128
  const [baseExpr, filters] = parseFilterChain(expr);
3154
3129
  if (filters.length > 0) {
@@ -3167,38 +3142,49 @@ function evalExpr(expr, context) {
3167
3142
  return value;
3168
3143
  }
3169
3144
  }
3170
- const fnMatch = expr.match(FN_CALL_RE);
3171
- if (fnMatch) {
3172
- const fnName = fnMatch[1];
3173
- const rawArgs = fnMatch[2] || "";
3174
- if (fnName.includes(".")) {
3175
- const lastDot = fnName.lastIndexOf(".");
3176
- const objPath = fnName.slice(0, lastDot);
3177
- const methodName = fnName.slice(lastDot + 1);
3178
- const obj = resolveVar(objPath, context);
3179
- if (obj && typeof obj === "object" && methodName in obj) {
3180
- const method = obj[methodName];
3181
- if (typeof method === "function") {
3182
- if (rawArgs.trim()) {
3183
- const parts = splitArgs(rawArgs);
3184
- const evalArgs = parts.map((a) => evalExpr(a.trim(), context));
3185
- return method.apply(obj, evalArgs);
3186
- }
3187
- return method.call(obj);
3188
- }
3189
- }
3190
- } else {
3191
- const fn = context[fnName] ?? resolveVar(fnName, context);
3192
- if (typeof fn === "function") {
3193
- if (rawArgs.trim()) {
3194
- const parts = splitArgs(rawArgs);
3195
- const evalArgs = parts.map((a) => evalExpr(a.trim(), context));
3196
- return fn(...evalArgs);
3197
- }
3198
- return fn();
3199
- }
3145
+ return EXPR_NOT_MATCHED;
3146
+ }
3147
+ function evaluateCallArgs(rawArgs, context) {
3148
+ return rawArgs.trim() ? splitArgs(rawArgs).map((arg) => evalExpr(arg.trim(), context)) : [];
3149
+ }
3150
+ function evalDottedFunction(name, rawArgs, context) {
3151
+ const lastDot = name.lastIndexOf(".");
3152
+ const owner = resolveVar(name.slice(0, lastDot), context);
3153
+ const member = name.slice(lastDot + 1);
3154
+ if (!owner || typeof owner !== "object" || !(member in owner)) {
3155
+ return EXPR_NOT_MATCHED;
3156
+ }
3157
+ const method = owner[member];
3158
+ return typeof method === "function" ? method.apply(owner, evaluateCallArgs(rawArgs, context)) : EXPR_NOT_MATCHED;
3159
+ }
3160
+ function evalFunctionExpression(expr, context) {
3161
+ const match = expr.match(FN_CALL_RE);
3162
+ if (!match) return EXPR_NOT_MATCHED;
3163
+ const name = match[1];
3164
+ const rawArgs = match[2] || "";
3165
+ if (name.includes(".")) return evalDottedFunction(name, rawArgs, context);
3166
+ const fn = context[name] ?? resolveVar(name, context);
3167
+ if (typeof fn === "function") return fn(...evaluateCallArgs(rawArgs, context));
3168
+ return EXPR_NOT_MATCHED;
3169
+ }
3170
+ function evalExpr(expr, context) {
3171
+ expr = expr.trim();
3172
+ const cachedForm = expressionFormCache.get(expr);
3173
+ if (cachedForm !== void 0) {
3174
+ if (cachedForm === -1) return resolveVar(expr, context);
3175
+ const result = EXPR_EVALUATORS[cachedForm](expr, context);
3176
+ return result === EXPR_NOT_MATCHED ? resolveVar(expr, context) : result;
3177
+ }
3178
+ for (let index = 0; index < EXPR_EVALUATORS.length; index++) {
3179
+ const result = EXPR_EVALUATORS[index](expr, context);
3180
+ if (result !== EXPR_NOT_MATCHED) {
3181
+ capCache(expressionFormCache, MEMO_CACHE_MAX);
3182
+ expressionFormCache.set(expr, index);
3183
+ return result;
3200
3184
  }
3201
3185
  }
3186
+ capCache(expressionFormCache, MEMO_CACHE_MAX);
3187
+ expressionFormCache.set(expr, FN_CALL_RE.test(expr) ? EXPR_EVALUATORS.length - 1 : -1);
3202
3188
  return resolveVar(expr, context);
3203
3189
  }
3204
3190
  function findTernary(expr) {
@@ -3654,7 +3640,7 @@ function _generateFormToken(descriptor = "") {
3654
3640
  function _generateFormTokenValue(descriptor = "") {
3655
3641
  return new SafeString(_buildFormTokenJwt(descriptor));
3656
3642
  }
3657
- var SafeString, KNOWN_TAGS, TERMINATOR_TAGS, GATEABLE_TAGS, BLOCK_TAG_ENDS, JSON_UNSAFE_RE, JSON_UNSAFE_MAP, NUMERIC_RE, METHOD_CALL_RE, FN_CALL_RE, IS_NOT_RE, IS_RE, NOT_IN_RE, IN_RE, DIVISIBLE_BY_RE, FILTER_WITH_ARGS_RE, FILTER_COMPARISON_RE, TITLE_WORD_RE, STRIP_TAGS_RE, FORMAT_RE, LEADING_WS_RE, TRAILING_WS_RE, THOUSANDS_RE, LIVE_RE, LIVE_WS_RE, LIVE_SRC_RE, EXTENDS_RE, EXTENDS_RE_GLOBAL, filterChainCache, pathParseCache, TEMPLATE_CACHE_MAX, MEMO_CACHE_MAX, TOKEN_RE, RAW_BLOCK_RE, VarRef, BUILTIN_FILTERS, _formTokenSessionId, Frond;
3643
+ var SafeString, KNOWN_TAGS, TERMINATOR_TAGS, GATEABLE_TAGS, BLOCK_TAG_ENDS, JSON_UNSAFE_RE, JSON_UNSAFE_MAP, NUMERIC_RE, METHOD_CALL_RE, FN_CALL_RE, IS_NOT_RE, IS_RE, NOT_IN_RE, IN_RE, DIVISIBLE_BY_RE, FILTER_WITH_ARGS_RE, FILTER_COMPARISON_RE, TITLE_WORD_RE, STRIP_TAGS_RE, FORMAT_RE, LEADING_WS_RE, TRAILING_WS_RE, THOUSANDS_RE, LIVE_RE, LIVE_WS_RE, LIVE_SRC_RE, EXTENDS_RE, EXTENDS_RE_GLOBAL, filterChainCache, pathParseCache, TEMPLATE_CACHE_MAX, MEMO_CACHE_MAX, TOKEN_RE, RAW_BLOCK_RE, EXPR_NOT_MATCHED, ARITHMETIC_OPERATIONS, EXPR_EVALUATORS, expressionFormCache, VarRef, BUILTIN_FILTERS, _formTokenSessionId, Frond;
3658
3644
  var init_engine = __esm({
3659
3645
  "../frond/src/engine.ts"() {
3660
3646
  "use strict";
@@ -3756,6 +3742,25 @@ var init_engine = __esm({
3756
3742
  MEMO_CACHE_MAX = 1024;
3757
3743
  TOKEN_RE = /(\{%-?\s*[\s\S]*?\s*-?%\})|(\{\{-?\s*[\s\S]*?\s*-?\}\})|(\{#[\s\S]*?#\})/g;
3758
3744
  RAW_BLOCK_RE = /\{%-?\s*raw\s*-?%\}([\s\S]*?)\{%-?\s*endraw\s*-?%\}/g;
3745
+ EXPR_NOT_MATCHED = Symbol("frond-expression-not-matched");
3746
+ ARITHMETIC_OPERATIONS = {
3747
+ "+": (left, right) => left + right,
3748
+ "-": (left, right) => left - right,
3749
+ "*": (left, right) => left * right,
3750
+ "//": (left, right) => right !== 0 ? Math.floor(left / right) : 0,
3751
+ "/": (left, right) => right !== 0 ? left / right : 0,
3752
+ "%": (left, right) => right !== 0 ? left % right : 0,
3753
+ "**": (left, right) => left ** right
3754
+ };
3755
+ EXPR_EVALUATORS = [
3756
+ evalPrimary,
3757
+ evalConditional,
3758
+ evalConcatOrComparison,
3759
+ evalArithmeticExpression,
3760
+ evalFilterExpression,
3761
+ evalFunctionExpression
3762
+ ];
3763
+ expressionFormCache = /* @__PURE__ */ new Map();
3759
3764
  VarRef = class {
3760
3765
  constructor(name) {
3761
3766
  this.name = name;
@@ -8819,14 +8824,14 @@ async function discoverRoutes(routesDir) {
8819
8824
  const currentMtime = statSync5(filePath).mtimeMs;
8820
8825
  if (_seenFiles.has(filePath) && _seenMtimes.get(filePath) === currentMtime) continue;
8821
8826
  const method = name.toUpperCase();
8822
- const relativePath3 = relative(routesDir, filePath);
8823
- const pattern = filePathToPattern(relativePath3);
8827
+ const relativePath2 = relative(routesDir, filePath);
8828
+ const pattern = filePathToPattern(relativePath2);
8824
8829
  try {
8825
8830
  const moduleUrl = `file://${filePath}?t=${currentMtime}`;
8826
8831
  const mod = await import(moduleUrl);
8827
8832
  const handler = mod.default ?? mod.handler;
8828
8833
  if (typeof handler !== "function") {
8829
- console.warn(` Warning: ${relativePath3} does not export a handler function, skipping`);
8834
+ console.warn(` Warning: ${relativePath2} does not export a handler function, skipping`);
8830
8835
  continue;
8831
8836
  }
8832
8837
  const meta = mod.meta;
@@ -8838,7 +8843,7 @@ async function discoverRoutes(routesDir) {
8838
8843
  _seenMtimes.set(filePath, currentMtime);
8839
8844
  registeredFromThisScan++;
8840
8845
  } catch (err) {
8841
- console.error(` Error loading route ${relativePath3}:`, err);
8846
+ console.error(` Error loading route ${relativePath2}:`, err);
8842
8847
  recordBrokenImport(filePath, err);
8843
8848
  }
8844
8849
  }
@@ -8872,8 +8877,8 @@ function recordBrokenImport(filePath, error) {
8872
8877
  } catch {
8873
8878
  }
8874
8879
  }
8875
- function filePathToPattern(relativePath3) {
8876
- const parts = relativePath3.replace(/\\/g, "/").split("/").slice(0, -1);
8880
+ function filePathToPattern(relativePath2) {
8881
+ const parts = relativePath2.replace(/\\/g, "/").split("/").slice(0, -1);
8877
8882
  const urlParts = parts.map((part) => {
8878
8883
  if (part.startsWith("[...") && part.endsWith("]")) {
8879
8884
  const name = part.slice(4, -1);
@@ -12110,497 +12115,135 @@ import * as fs3 from "node:fs";
12110
12115
  import * as path2 from "node:path";
12111
12116
  import { spawnSync } from "node:child_process";
12112
12117
  import { fileURLToPath } from "node:url";
12113
- function walkFiles(dir, extensions, exclude = ["node_modules", ".git", "dist", "build"]) {
12114
- const results = [];
12115
- if (!fs3.existsSync(dir)) return results;
12116
- const entries = fs3.readdirSync(dir, { withFileTypes: true });
12117
- for (const entry of entries) {
12118
- const fullPath = path2.join(dir, entry.name);
12119
- if (entry.isDirectory()) {
12120
- if (!exclude.includes(entry.name)) {
12121
- results.push(...walkFiles(fullPath, extensions, exclude));
12122
- }
12123
- } else if (entry.isFile()) {
12124
- const ext = path2.extname(entry.name);
12125
- if (extensions.includes(ext) && !entry.name.endsWith(".d.ts")) {
12126
- results.push(fullPath);
12127
- }
12128
- }
12129
- }
12130
- return results;
12131
- }
12132
- function readFileSafe(filePath) {
12133
- try {
12134
- return fs3.readFileSync(filePath, "utf-8");
12135
- } catch {
12136
- return null;
12137
- }
12138
- }
12139
- function relativePath(filePath, root = ".") {
12140
- return path2.relative(root, filePath);
12141
- }
12142
- function countLines(source) {
12143
- const lines = source.split("\n");
12144
- let loc = 0;
12145
- let blank = 0;
12146
- let comment = 0;
12147
- let inBlockComment = false;
12148
- for (const line of lines) {
12149
- const stripped = line.trim();
12150
- if (!stripped) {
12151
- blank++;
12152
- continue;
12153
- }
12154
- if (inBlockComment) {
12155
- comment++;
12156
- if (stripped.includes("*/")) {
12157
- inBlockComment = false;
12158
- }
12159
- continue;
12160
- }
12161
- if (stripped.startsWith("/*")) {
12162
- comment++;
12163
- if (!stripped.includes("*/") || stripped.endsWith("/*")) {
12164
- inBlockComment = true;
12165
- }
12166
- continue;
12167
- }
12168
- if (stripped.startsWith("//")) {
12169
- comment++;
12170
- continue;
12171
- }
12172
- loc++;
12173
- }
12174
- return { loc, blank, comment };
12175
- }
12176
- function stripLiterals(source) {
12177
- const out = [];
12178
- const n = source.length;
12179
- let i = 0;
12180
- let prevSignificant = "";
12181
- let prevWord = "";
12182
- const regexKeywords = /* @__PURE__ */ new Set([
12183
- "return",
12184
- "typeof",
12185
- "instanceof",
12186
- "in",
12187
- "of",
12188
- "new",
12189
- "delete",
12190
- "void",
12191
- "throw",
12192
- "case",
12193
- "do",
12194
- "else",
12195
- "yield",
12196
- "await"
12197
- ]);
12198
- function prevEndsExpression() {
12199
- if (prevSignificant === "") return false;
12200
- if (/[A-Za-z0-9_$]/.test(prevSignificant)) {
12201
- return !regexKeywords.has(prevWord);
12202
- }
12203
- if (prevSignificant === ")" || prevSignificant === "]") return true;
12204
- if (prevSignificant === ".") return true;
12205
- return false;
12206
- }
12207
- while (i < n) {
12208
- const ch = source[i];
12209
- const next = i + 1 < n ? source[i + 1] : "";
12210
- if (ch === "/" && next === "/") {
12211
- out.push("//");
12212
- i += 2;
12213
- while (i < n && source[i] !== "\n") {
12214
- out.push(" ");
12215
- i++;
12216
- }
12217
- continue;
12218
- }
12219
- if (ch === "/" && next === "*") {
12220
- out.push("/*");
12221
- i += 2;
12222
- while (i < n && !(source[i] === "*" && source[i + 1] === "/")) {
12223
- out.push(source[i] === "\n" ? "\n" : " ");
12224
- i++;
12225
- }
12226
- if (i < n) {
12227
- out.push("*/");
12228
- i += 2;
12229
- }
12230
- continue;
12231
- }
12232
- if (ch === '"' || ch === "'") {
12233
- const quote = ch;
12234
- out.push(quote);
12235
- i++;
12236
- while (i < n && source[i] !== quote) {
12237
- if (source[i] === "\\" && i + 1 < n) {
12238
- out.push(" ");
12239
- i += 2;
12240
- continue;
12241
- }
12242
- if (source[i] === "\n") {
12243
- out.push("\n");
12244
- i++;
12245
- break;
12246
- }
12247
- out.push(" ");
12248
- i++;
12249
- }
12250
- if (i < n && source[i] === quote) {
12251
- out.push(quote);
12252
- i++;
12253
- }
12254
- prevSignificant = quote;
12255
- prevWord = "";
12256
- continue;
12257
- }
12258
- if (ch === "`") {
12259
- out.push("`");
12260
- i++;
12261
- while (i < n && source[i] !== "`") {
12262
- if (source[i] === "\\" && i + 1 < n) {
12263
- out.push(source[i + 1] === "\n" ? " \n" : " ");
12264
- i += 2;
12265
- continue;
12266
- }
12267
- if (source[i] === "$" && source[i + 1] === "{") {
12268
- out.push("${");
12269
- i += 2;
12270
- let depth = 1;
12271
- const exprStart = i;
12272
- while (i < n && depth > 0) {
12273
- if (source[i] === "{") depth++;
12274
- else if (source[i] === "}") depth--;
12275
- if (depth === 0) break;
12276
- i++;
12277
- }
12278
- out.push(stripLiterals(source.slice(exprStart, i)));
12279
- if (i < n && source[i] === "}") {
12280
- out.push("}");
12281
- i++;
12282
- }
12283
- continue;
12284
- }
12285
- out.push(source[i] === "\n" ? "\n" : " ");
12286
- i++;
12287
- }
12288
- if (i < n && source[i] === "`") {
12289
- out.push("`");
12290
- i++;
12291
- }
12292
- prevSignificant = "`";
12293
- prevWord = "";
12294
- continue;
12295
- }
12296
- if (ch === "/" && !prevEndsExpression()) {
12297
- let j = i + 1;
12298
- let ok = false;
12299
- let inClass = false;
12300
- while (j < n) {
12301
- const c = source[j];
12302
- if (c === "\\") {
12303
- j += 2;
12304
- continue;
12305
- }
12306
- if (c === "\n") break;
12307
- if (c === "[") inClass = true;
12308
- else if (c === "]") inClass = false;
12309
- else if (c === "/" && !inClass) {
12310
- ok = true;
12311
- break;
12312
- }
12313
- j++;
12314
- }
12315
- if (ok) {
12316
- out.push("/");
12317
- for (let k = i + 1; k < j; k++) out.push(" ");
12318
- out.push("/");
12319
- i = j + 1;
12320
- while (i < n && /[a-z]/i.test(source[i])) {
12321
- out.push(source[i]);
12322
- i++;
12323
- }
12324
- prevSignificant = "/";
12325
- prevWord = "";
12326
- continue;
12327
- }
12328
- }
12329
- out.push(ch);
12330
- if (!/\s/.test(ch)) {
12331
- prevSignificant = ch;
12332
- if (/[A-Za-z0-9_$]/.test(ch)) {
12333
- prevWord = /[A-Za-z0-9_$]/.test(source[i - 1] ?? "") ? prevWord + ch : ch;
12334
- } else {
12335
- prevWord = "";
12336
- }
12337
- }
12338
- i++;
12339
- }
12340
- return out.join("");
12341
- }
12342
- function countClassesQuick(source) {
12343
- const matches = source.match(
12344
- /(?:^|\n)\s*(?:export\s+)?(?:abstract\s+)?class\s+\w+/g
12345
- );
12346
- return matches ? matches.length : 0;
12347
- }
12348
- function countFunctionsQuick(source) {
12349
- const clean = stripLiterals(source);
12350
- let count = 0;
12351
- const funcDecls = clean.match(
12352
- /(?:^|\n)\s*(?:export\s+)?(?:async\s+)?function\s+\w+\s*\(/g
12353
- );
12354
- if (funcDecls) count += funcDecls.length;
12355
- const methods = clean.match(
12356
- /(?:^|\n)\s*(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?\w+\s*\([^)]*\)\s*(?::\s*\S+)?\s*\{/g
12357
- );
12358
- if (methods) count += methods.length;
12359
- const arrows = clean.match(
12360
- /(?:^|\n)\s*(?:export\s+)?(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?\(/g
12361
- );
12362
- if (arrows) count += arrows.length;
12363
- return count;
12364
- }
12365
- function resolveRoot(root = "src") {
12366
- const rootPath = path2.resolve(root);
12367
- if (fs3.existsSync(rootPath) && walkFiles(rootPath, [".ts", ".js"]).length > 0) {
12368
- _lastScanRoot = rootPath;
12369
- return root;
12370
- }
12371
- const fwDir = path2.resolve(path2.dirname(new URL(import.meta.url).pathname));
12372
- _lastScanRoot = fwDir;
12373
- return fwDir;
12374
- }
12375
- function quickMetrics(root = "src") {
12376
- root = resolveRoot(root);
12377
- const rootPath = path2.resolve(root);
12378
- if (!fs3.existsSync(rootPath)) {
12379
- return { error: `Directory not found: ${root}` };
12380
- }
12381
- const tsFiles = walkFiles(rootPath, [".ts", ".js"]);
12382
- const twigFiles = walkFiles(rootPath, [".twig", ".html"]);
12383
- const migrationsDir = path2.resolve("migrations");
12384
- const migrationFiles = [
12385
- ...walkFiles(migrationsDir, [".sql"]),
12386
- ...walkFiles(migrationsDir, [".ts"])
12387
- ];
12388
- const scssFiles = walkFiles(rootPath, [".scss", ".css"]);
12389
- let totalLoc = 0;
12390
- let totalBlank = 0;
12391
- let totalComment = 0;
12392
- let totalClasses = 0;
12393
- let totalFunctions = 0;
12394
- const fileDetails = [];
12395
- for (const f of tsFiles) {
12396
- const source = readFileSafe(f);
12397
- if (source === null) continue;
12398
- const counts = countLines(source);
12399
- const classes = countClassesQuick(source);
12400
- const functions = countFunctionsQuick(source);
12401
- totalLoc += counts.loc;
12402
- totalBlank += counts.blank;
12403
- totalComment += counts.comment;
12404
- totalClasses += classes;
12405
- totalFunctions += functions;
12406
- fileDetails.push({
12407
- path: relativePath(f, rootPath),
12408
- loc: counts.loc,
12409
- blank: counts.blank,
12410
- comment: counts.comment,
12411
- classes,
12412
- functions
12413
- });
12118
+ function containsTypeScript(directory) {
12119
+ if (!fs3.existsSync(directory) || !fs3.statSync(directory).isDirectory()) return false;
12120
+ for (const entry of fs3.readdirSync(directory, { withFileTypes: true })) {
12121
+ if (["node_modules", ".git", "dist", "build"].includes(entry.name)) continue;
12122
+ const target = path2.join(directory, entry.name);
12123
+ if (entry.isDirectory() ? containsTypeScript(target) : /\.[cm]?[jt]sx?$/.test(entry.name)) return true;
12414
12124
  }
12415
- fileDetails.sort((a, b) => b.loc - a.loc);
12416
- let routeCount = 0;
12417
- let ormCount = 0;
12418
- for (const f of tsFiles) {
12419
- const source = readFileSafe(f);
12420
- if (source === null) continue;
12421
- const routes = source.match(
12422
- /(?:router\s*\.\s*(?:get|post|put|delete|patch|any)\s*\(|@(?:get|post|put|delete|patch)\s*\()/g
12423
- );
12424
- if (routes) routeCount += routes.length;
12425
- const orms = source.match(
12426
- /class\s+\w+\s+extends\s+(?:ORM|Model)\b/g
12427
- );
12428
- if (orms) ormCount += orms.length;
12429
- }
12430
- const breakdown = {
12431
- typescript: tsFiles.filter((f) => f.endsWith(".ts")).length,
12432
- javascript: tsFiles.filter((f) => f.endsWith(".js")).length,
12433
- templates: twigFiles.length,
12434
- migrations: migrationFiles.length,
12435
- stylesheets: scssFiles.length
12436
- };
12437
- return {
12438
- file_count: tsFiles.length,
12439
- total_loc: totalLoc,
12440
- total_blank: totalBlank,
12441
- total_comment: totalComment,
12442
- lloc: totalLoc,
12443
- classes: totalClasses,
12444
- functions: totalFunctions,
12445
- route_count: routeCount,
12446
- orm_count: ormCount,
12447
- template_count: twigFiles.length,
12448
- migration_count: migrationFiles.length,
12449
- avg_file_size: tsFiles.length > 0 ? Math.round(totalLoc / tsFiles.length * 10) / 10 : 0,
12450
- largest_files: fileDetails.slice(0, 10),
12451
- breakdown
12452
- };
12125
+ return false;
12453
12126
  }
12454
- function resolveScanTarget(root = "src") {
12455
- const resolved = resolveRoot(root);
12456
- const frameworkDir = path2.dirname(fileURLToPath(import.meta.url));
12457
- const real = path2.resolve(resolved);
12458
- const scanningFramework = real === frameworkDir || real.startsWith(frameworkDir);
12459
- return [resolved, scanningFramework ? "framework" : "project"];
12127
+ function resolveTarget(root = "src") {
12128
+ const resolved = containsTypeScript(root) ? path2.resolve(root) : path2.dirname(fileURLToPath(import.meta.url));
12129
+ const mode = containsTypeScript(root) ? "project" : "framework";
12130
+ lastScanRoot = resolved;
12131
+ return [resolved, mode];
12460
12132
  }
12461
12133
  function enginePath() {
12462
- const names = process.platform === "win32" ? ["tina4.exe", "tina4.cmd", "tina4"] : ["tina4"];
12463
- for (const dir of (process.env.PATH || "").split(path2.delimiter)) {
12464
- if (!dir) continue;
12134
+ const names = process.platform === "win32" ? ["tina4.exe", "tina4"] : ["tina4"];
12135
+ for (const directory of (process.env.PATH || "").split(path2.delimiter)) {
12465
12136
  for (const name of names) {
12466
- const candidate = path2.join(dir, name);
12137
+ const candidate = path2.join(directory, name);
12467
12138
  try {
12468
- if (!fs3.statSync(candidate).isFile()) continue;
12469
12139
  fs3.accessSync(candidate, fs3.constants.X_OK);
12140
+ if (!fs3.statSync(candidate).isFile()) continue;
12141
+ const descriptor = fs3.openSync(candidate, "r");
12142
+ const header = Buffer.alloc(2);
12143
+ fs3.readSync(descriptor, header, 0, 2, 0);
12144
+ fs3.closeSync(descriptor);
12145
+ if (header.toString("latin1") !== "#!") return candidate;
12470
12146
  } catch {
12471
12147
  continue;
12472
12148
  }
12473
- try {
12474
- const fd = fs3.openSync(candidate, "r");
12475
- const buf = Buffer.alloc(2);
12476
- fs3.readSync(fd, buf, 0, 2, 0);
12477
- fs3.closeSync(fd);
12478
- if (buf.toString("latin1") === "#!") continue;
12479
- } catch {
12480
- }
12481
- return candidate;
12482
12149
  }
12483
12150
  }
12484
12151
  return null;
12485
12152
  }
12486
12153
  function runEngine(target) {
12487
12154
  const binary = enginePath();
12488
- if (binary === null) {
12489
- throw new MetricsEngineError(`tina4 not found on PATH - ${INSTALL_HINT}`);
12490
- }
12491
- const proc = spawnSync(binary, ["metrics", "--path", target, "--json"], {
12155
+ if (!binary) throw new MetricsEngineError(`tina4 not found on PATH - ${INSTALL_HINT}`);
12156
+ const processResult = spawnSync(binary, ["metrics", "--path", target, "--json"], {
12492
12157
  encoding: "utf8",
12493
- timeout: TIMEOUT_MS,
12158
+ timeout: 6e4,
12494
12159
  maxBuffer: 64 * 1024 * 1024
12495
12160
  });
12496
- if (proc.error) {
12497
- const err = proc.error;
12498
- if (err.code === "ETIMEDOUT") {
12499
- throw new MetricsEngineError(`tina4 metrics timed out after ${TIMEOUT_MS / 1e3}s on ${target}`);
12500
- }
12501
- throw new MetricsEngineError(`could not run ${binary}: ${err.message}`);
12502
- }
12503
- if (proc.status !== 0) {
12504
- const detail = (proc.stderr || proc.stdout || "").trim().split("\n")[0];
12505
- throw new MetricsEngineError(
12506
- `tina4 metrics failed on ${target}: ${detail || `exit code ${proc.status}`}`
12507
- );
12161
+ if (processResult.error) {
12162
+ throw new MetricsEngineError(`could not run ${binary}: ${processResult.error.message}`);
12508
12163
  }
12509
- if (!proc.stdout || !proc.stdout.trim()) {
12510
- throw new MetricsEngineError(`tina4 metrics produced no output for ${target}`);
12164
+ if (processResult.status !== 0) {
12165
+ const detail = (processResult.stderr || processResult.stdout || "").trim().split("\n")[0];
12166
+ throw new MetricsEngineError(`tina4 metrics failed on ${target}: ${detail || processResult.status}`);
12511
12167
  }
12512
- let payload;
12513
12168
  try {
12514
- payload = JSON.parse(proc.stdout);
12515
- } catch (e) {
12516
- throw new MetricsEngineError(`tina4 metrics returned unreadable JSON: ${e.message}`);
12517
- }
12518
- if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
12519
- throw new MetricsEngineError("tina4 metrics returned a non-object payload");
12169
+ const payload = JSON.parse(processResult.stdout);
12170
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
12171
+ throw new Error("non-object payload");
12172
+ }
12173
+ return payload;
12174
+ } catch (error) {
12175
+ throw new MetricsEngineError(`tina4 metrics returned unreadable JSON: ${error.message}`);
12520
12176
  }
12521
- return payload;
12522
12177
  }
12523
- function requireKey(payload, key, isArray) {
12524
- const value = payload[key];
12525
- const ok = isArray ? Array.isArray(value) : value !== null && typeof value === "object" && !Array.isArray(value);
12526
- if (!ok) {
12527
- throw new MetricsEngineError(
12528
- `engine payload has no usable '${key}' - the installed tina4 CLI predates a field the dashboard renders. Update it: ${INSTALL_HINT}`
12529
- );
12178
+ function requireArray(payload, key) {
12179
+ if (!Array.isArray(payload[key])) {
12180
+ throw new MetricsEngineError(`engine payload has no usable '${key}' - ${INSTALL_HINT}`);
12530
12181
  }
12531
- return value;
12182
+ return payload[key];
12532
12183
  }
12533
12184
  function fullAnalysis(root = "src") {
12534
- const [resolved, scanMode] = resolveScanTarget(root);
12185
+ const [resolved, scanMode] = resolveTarget(root);
12535
12186
  const payload = runEngine(resolved);
12536
- const summary = requireKey(payload, "summary", false);
12537
- const fileMetrics = requireKey(payload, "file_metrics", true);
12538
- const functions = requireKey(payload, "most_complex_functions", true);
12539
- const missing = SUMMARY_KEYS.filter((k) => !(k in summary));
12540
- if (missing.length) {
12541
- throw new MetricsEngineError(
12542
- `engine summary is missing ${missing.join(", ")} - update the CLI: ${INSTALL_HINT}`
12543
- );
12544
- }
12545
- if (fileMetrics.length) {
12546
- const absent = FILE_KEYS.filter((k) => !(k in fileMetrics[0]));
12547
- if (absent.length) throw new MetricsEngineError(`engine file_metrics is missing ${absent.join(", ")}`);
12187
+ const summary = payload.summary;
12188
+ if (!summary || typeof summary !== "object" || Array.isArray(summary)) {
12189
+ throw new MetricsEngineError(`engine payload has no usable 'summary' - ${INSTALL_HINT}`);
12190
+ }
12191
+ const fileMetrics = requireArray(payload, "file_metrics");
12192
+ const functions = requireArray(payload, "most_complex_functions");
12193
+ const missingSummary = SUMMARY_KEYS.filter((key) => !(key in summary));
12194
+ if (missingSummary.length) throw new MetricsEngineError(`engine summary is missing ${missingSummary.join(", ")}`);
12195
+ const missingFile = fileMetrics.length ? FILE_KEYS.filter((key) => !(key in fileMetrics[0])) : [];
12196
+ if (missingFile.length) throw new MetricsEngineError(`engine file_metrics is missing ${missingFile.join(", ")}`);
12197
+ const missingFunction = functions.length ? FUNCTION_KEYS.filter((key) => !(key in functions[0])) : [];
12198
+ if (missingFunction.length) {
12199
+ throw new MetricsEngineError(`engine function metrics are missing ${missingFunction.join(", ")}`);
12548
12200
  }
12549
- if (functions.length) {
12550
- const absent = FUNCTION_KEYS.filter((k) => !(k in functions[0]));
12551
- if (absent.length) throw new MetricsEngineError(`engine function metrics are missing ${absent.join(", ")}`);
12552
- }
12553
- const result = {};
12554
- for (const key of SUMMARY_KEYS) result[key] = summary[key];
12555
- result.file_metrics = fileMetrics;
12556
- result.most_complex_functions = functions.slice(0, 15);
12557
- result.dependency_graph = payload.dependency_graph || {};
12558
- result.scan_mode = scanMode;
12559
- result.scan_root = path2.resolve(resolved);
12560
- result.engine = "tina4-cli";
12561
- return result;
12201
+ return {
12202
+ ...Object.fromEntries(SUMMARY_KEYS.map((key) => [key, summary[key]])),
12203
+ file_metrics: fileMetrics,
12204
+ most_complex_functions: functions.slice(0, 15),
12205
+ dependency_graph: payload.dependency_graph || {},
12206
+ scan_mode: scanMode,
12207
+ scan_root: resolved,
12208
+ engine: "tina4-cli"
12209
+ };
12562
12210
  }
12563
12211
  function fileDetail(filePath) {
12564
12212
  if (!filePath) throw new MetricsEngineError("fileDetail needs a path");
12565
12213
  let target = filePath;
12566
- if (!fs3.existsSync(target) && _lastScanRoot) {
12567
- const candidate = path2.join(_lastScanRoot, filePath);
12568
- if (fs3.existsSync(candidate)) target = candidate;
12569
- }
12214
+ if (!fs3.existsSync(target) && lastScanRoot) target = path2.join(lastScanRoot, filePath);
12570
12215
  if (!fs3.existsSync(target)) throw new MetricsEngineError(`no such file: ${filePath}`);
12571
12216
  if (fs3.statSync(target).isDirectory()) throw new MetricsEngineError(`not a file: ${filePath}`);
12572
12217
  const payload = runEngine(target);
12573
- const fileMetrics = requireKey(payload, "file_metrics", true);
12574
- if (!fileMetrics.length) {
12575
- throw new MetricsEngineError(`engine reported no metrics for ${filePath}`);
12576
- }
12577
- return { ...fileMetrics[0], engine: "tina4-cli" };
12218
+ const files = requireArray(payload, "file_metrics");
12219
+ if (!files.length) throw new MetricsEngineError(`engine reported no metrics for ${filePath}`);
12220
+ return {
12221
+ ...files[0],
12222
+ function_count: files[0].functions || 0,
12223
+ functions: requireArray(payload, "most_complex_functions"),
12224
+ engine: "tina4-cli"
12225
+ };
12578
12226
  }
12579
- var _lastScanRoot, MetricsEngineError, TIMEOUT_MS, INSTALL_HINT, SUMMARY_KEYS, FILE_KEYS, FUNCTION_KEYS;
12227
+ var lastScanRoot, MetricsEngineError, INSTALL_HINT, SUMMARY_KEYS, FILE_KEYS, FUNCTION_KEYS;
12580
12228
  var init_metrics = __esm({
12581
12229
  "../core/src/metrics.ts"() {
12582
12230
  "use strict";
12583
- _lastScanRoot = "";
12231
+ lastScanRoot = "";
12584
12232
  MetricsEngineError = class extends Error {
12585
12233
  constructor(message) {
12586
12234
  super(message);
12587
12235
  this.name = "MetricsEngineError";
12588
12236
  }
12589
12237
  };
12590
- TIMEOUT_MS = 6e4;
12591
- INSTALL_HINT = [
12592
- "the tina4 CLI provides the metrics engine (ADR-0002). Install it with",
12593
- " curl -fsSL https://tina4.com/install.sh | sh",
12594
- "or see https://tina4.com/cli"
12595
- ].join("\n");
12238
+ INSTALL_HINT = "update the native tina4 CLI: https://tina4.com/cli";
12596
12239
  SUMMARY_KEYS = ["files_analyzed", "total_functions", "avg_complexity", "avg_maintainability"];
12597
- FILE_KEYS = ["path", "loc", "avg_complexity", "maintainability", "has_tests"];
12240
+ FILE_KEYS = ["path", "loc", "avg_complexity", "maintainability", "has_referencing_test"];
12598
12241
  FUNCTION_KEYS = ["name", "file", "line", "complexity", "loc"];
12599
12242
  }
12600
12243
  });
12601
12244
 
12602
12245
  // ../core/src/feedback.ts
12603
- import { readFileSync as readFileSync11, existsSync as existsSync11 } from "node:fs";
12246
+ import { readFileSync as readFileSync10, existsSync as existsSync11 } from "node:fs";
12604
12247
  import { dirname as dirname5, join as join13, resolve as resolve5 } from "node:path";
12605
12248
  import { fileURLToPath as fileURLToPath2 } from "node:url";
12606
12249
  function feedbackEnabled() {
@@ -12741,7 +12384,7 @@ var init_feedback = __esm({
12741
12384
  handleFeedbackWidgetJs = (_req, res) => {
12742
12385
  let body;
12743
12386
  if (existsSync11(WIDGET_BUNDLE_PATH)) {
12744
- body = readFileSync11(WIDGET_BUNDLE_PATH);
12387
+ body = readFileSync10(WIDGET_BUNDLE_PATH);
12745
12388
  } else {
12746
12389
  body = "console.warn('tina4-feedback-widget bundle not built yet');";
12747
12390
  }
@@ -12756,7 +12399,7 @@ var init_feedback = __esm({
12756
12399
  });
12757
12400
 
12758
12401
  // ../core/src/version.ts
12759
- import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
12402
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
12760
12403
  import { dirname as dirname6, join as join14 } from "node:path";
12761
12404
  import { fileURLToPath as fileURLToPath3 } from "node:url";
12762
12405
  function resolveFrameworkVersion() {
@@ -12765,7 +12408,7 @@ function resolveFrameworkVersion() {
12765
12408
  const pkgPath = join14(dir, "package.json");
12766
12409
  if (existsSync12(pkgPath)) {
12767
12410
  try {
12768
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
12411
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
12769
12412
  if (typeof pkg.version === "string" && pkg.version) return pkg.version;
12770
12413
  } catch {
12771
12414
  }
@@ -15553,8 +15196,8 @@ __export(context_exports, {
15553
15196
  fts5Supported: () => fts5Supported
15554
15197
  });
15555
15198
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
15556
- import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync14, readdirSync as readdirSync7, realpathSync as realpathSync5 } from "node:fs";
15557
- import { basename as basename4, dirname as dirname8, extname as extname5, isAbsolute as isAbsolute4, join as join16, relative as relative4, resolve as resolve7 } from "node:path";
15199
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync13, readdirSync as readdirSync7, realpathSync as realpathSync5 } from "node:fs";
15200
+ import { basename as basename4, dirname as dirname8, extname as extname4, isAbsolute as isAbsolute4, join as join16, relative as relative3, resolve as resolve7 } from "node:path";
15558
15201
  function fts5Supported() {
15559
15202
  try {
15560
15203
  const conn = new DatabaseSync2(":memory:");
@@ -15689,7 +15332,7 @@ var init_context = __esm({
15689
15332
  }
15690
15333
  // ── indexing ───────────────────────────────────────────────
15691
15334
  static chunksFor(label, text) {
15692
- const ext = extname5(label).toLowerCase();
15335
+ const ext = extname4(label).toLowerCase();
15693
15336
  const special = SPECIAL_FILES.has(basename4(label).toLowerCase());
15694
15337
  if (CODE_EXTS.has(ext) || CONFIG_EXTS.has(ext) || special) {
15695
15338
  return chunkCode(text, label);
@@ -15707,7 +15350,7 @@ var init_context = __esm({
15707
15350
  const stored = label != null ? String(label) : String(file);
15708
15351
  let text;
15709
15352
  try {
15710
- text = readFileSync14(file, "utf-8");
15353
+ text = readFileSync13(file, "utf-8");
15711
15354
  } catch {
15712
15355
  return 0;
15713
15356
  }
@@ -15728,7 +15371,7 @@ var init_context = __esm({
15728
15371
  static eligible(filename) {
15729
15372
  const fn = filename.toLowerCase();
15730
15373
  if (fn.endsWith(".min.js")) return false;
15731
- const ext = extname5(fn);
15374
+ const ext = extname4(fn);
15732
15375
  return CODE_EXTS.has(ext) || DOC_EXTS.has(ext) || CONFIG_EXTS.has(ext) || SPECIAL_FILES.has(fn);
15733
15376
  }
15734
15377
  /**
@@ -15757,7 +15400,7 @@ var init_context = __esm({
15757
15400
  for (const fn of files) {
15758
15401
  if (!_Context.eligible(fn)) continue;
15759
15402
  const full = join16(dir, fn);
15760
- const rel = relative4(rootAbs, full);
15403
+ const rel = relative3(rootAbs, full);
15761
15404
  total += this.indexPath(full, rel);
15762
15405
  }
15763
15406
  for (const d of subdirs) walk2(join16(dir, d));
@@ -15778,7 +15421,7 @@ var init_context = __esm({
15778
15421
  const raw = String(changedPath);
15779
15422
  const abs = isAbsolute4(raw) ? raw : join16(process.cwd(), raw);
15780
15423
  const resolved = realResolve(resolve7(abs));
15781
- const rel = relative4(this.root, resolved);
15424
+ const rel = relative3(this.root, resolved);
15782
15425
  if (rel === "" || rel.startsWith("..") || isAbsolute4(rel)) {
15783
15426
  return -1;
15784
15427
  }
@@ -17700,7 +17343,7 @@ var init_job = __esm({
17700
17343
  });
17701
17344
 
17702
17345
  // ../core/src/queueBackends/liteBackend.ts
17703
- import { mkdirSync as mkdirSync10, readdirSync as readdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync8, unlinkSync as unlinkSync6, existsSync as existsSync15 } from "node:fs";
17346
+ import { mkdirSync as mkdirSync10, readdirSync as readdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync8, unlinkSync as unlinkSync6, existsSync as existsSync15 } from "node:fs";
17704
17347
  import { join as join17 } from "node:path";
17705
17348
  import { randomUUID as randomUUID5 } from "node:crypto";
17706
17349
  var LiteBackend;
@@ -17800,7 +17443,7 @@ var init_liteBackend = __esm({
17800
17443
  const filePath = join17(dir, filename);
17801
17444
  let job;
17802
17445
  try {
17803
- job = JSON.parse(readFileSync15(filePath, "utf-8"));
17446
+ job = JSON.parse(readFileSync14(filePath, "utf-8"));
17804
17447
  } catch {
17805
17448
  continue;
17806
17449
  }
@@ -17864,7 +17507,7 @@ var init_liteBackend = __esm({
17864
17507
  const filePath = join17(reservedDir, filename);
17865
17508
  let record;
17866
17509
  try {
17867
- record = JSON.parse(readFileSync15(filePath, "utf-8"));
17510
+ record = JSON.parse(readFileSync14(filePath, "utf-8"));
17868
17511
  } catch {
17869
17512
  continue;
17870
17513
  }
@@ -17977,7 +17620,7 @@ var init_liteBackend = __esm({
17977
17620
  let count = 0;
17978
17621
  for (const file of files) {
17979
17622
  try {
17980
- const job = JSON.parse(readFileSync15(join17(scanDir, file), "utf-8"));
17623
+ const job = JSON.parse(readFileSync14(join17(scanDir, file), "utf-8"));
17981
17624
  if (job.status === status2) count++;
17982
17625
  } catch {
17983
17626
  }
@@ -18034,7 +17677,7 @@ var init_liteBackend = __esm({
18034
17677
  const files = readdirSync8(dir).filter((f) => f.endsWith(".queue-data")).sort();
18035
17678
  for (const file of files) {
18036
17679
  try {
18037
- const job = JSON.parse(readFileSync15(join17(dir, file), "utf-8"));
17680
+ const job = JSON.parse(readFileSync14(join17(dir, file), "utf-8"));
18038
17681
  const attempts = job.attempts || 0;
18039
17682
  if (attempts > 0 && attempts < maxRetries) {
18040
17683
  results.push(job);
@@ -18060,7 +17703,7 @@ var init_liteBackend = __esm({
18060
17703
  const failedDir = join17(this.basePath, q, "failed");
18061
17704
  const filePath = join17(failedDir, `${jobId}.queue-data`);
18062
17705
  if (existsSync15(filePath)) {
18063
- const job = JSON.parse(readFileSync15(filePath, "utf-8"));
17706
+ const job = JSON.parse(readFileSync14(filePath, "utf-8"));
18064
17707
  job.status = "pending";
18065
17708
  job.attempts = (job.attempts || 0) + 1;
18066
17709
  job.error = void 0;
@@ -18084,7 +17727,7 @@ var init_liteBackend = __esm({
18084
17727
  const files = readdirSync8(failedDir).filter((f) => f.endsWith(".queue-data")).sort();
18085
17728
  for (const file of files) {
18086
17729
  try {
18087
- const job = JSON.parse(readFileSync15(join17(failedDir, file), "utf-8"));
17730
+ const job = JSON.parse(readFileSync14(join17(failedDir, file), "utf-8"));
18088
17731
  if ((job.attempts || 0) >= maxRetries) {
18089
17732
  job.status = "dead";
18090
17733
  results.push(job);
@@ -18118,7 +17761,7 @@ var init_liteBackend = __esm({
18118
17761
  const files = readdirSync8(dir).filter((f) => f.endsWith(".queue-data"));
18119
17762
  for (const file of files) {
18120
17763
  try {
18121
- const job = JSON.parse(readFileSync15(join17(dir, file), "utf-8"));
17764
+ const job = JSON.parse(readFileSync14(join17(dir, file), "utf-8"));
18122
17765
  if (job.status === status2) {
18123
17766
  unlinkSync6(join17(dir, file));
18124
17767
  count++;
@@ -18145,7 +17788,7 @@ var init_liteBackend = __esm({
18145
17788
  for (const file of files) {
18146
17789
  try {
18147
17790
  const filePath = join17(failedDir, file);
18148
- const job = JSON.parse(readFileSync15(filePath, "utf-8"));
17791
+ const job = JSON.parse(readFileSync14(filePath, "utf-8"));
18149
17792
  if ((job.attempts || 0) >= maxRetries) {
18150
17793
  continue;
18151
17794
  }
@@ -18177,7 +17820,7 @@ var init_liteBackend = __esm({
18177
17820
  const filePath = join17(dir, file);
18178
17821
  let job;
18179
17822
  try {
18180
- job = JSON.parse(readFileSync15(filePath, "utf-8"));
17823
+ job = JSON.parse(readFileSync14(filePath, "utf-8"));
18181
17824
  } catch {
18182
17825
  continue;
18183
17826
  }
@@ -19640,7 +19283,7 @@ function detectVersion(projectRoot3) {
19640
19283
  }
19641
19284
  return "0.0.0";
19642
19285
  }
19643
- function relativePath2(absPath, projectRoot3, frameworkRoots) {
19286
+ function relativePath(absPath, projectRoot3, frameworkRoots) {
19644
19287
  const norm = path6.resolve(absPath);
19645
19288
  for (const fw of frameworkRoots) {
19646
19289
  const parent = path6.dirname(fw);
@@ -20105,7 +19748,7 @@ function buildEntriesForFile(absPath, source, fwRoots, projectRoot3, version, ou
20105
19748
  } catch {
20106
19749
  return;
20107
19750
  }
20108
- const rel = relativePath2(absPath, projectRoot3, fwRoots);
19751
+ const rel = relativePath(absPath, projectRoot3, fwRoots);
20109
19752
  for (const cls of parsed.classes) {
20110
19753
  if (!cls.exported && source === "framework") {
20111
19754
  continue;
@@ -20741,8 +20384,8 @@ ${end}
20741
20384
 
20742
20385
  // ../core/src/devAdmin.ts
20743
20386
  import { cpus as osCpus } from "node:os";
20744
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync12, existsSync as existsSync19, readdirSync as readdirSync12, mkdirSync as mkdirSync13, copyFileSync, statSync as statSync14 } from "node:fs";
20745
- import { join as join21, dirname as dirname10, resolve as resolve11, relative as relative8 } from "node:path";
20387
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, existsSync as existsSync19, readdirSync as readdirSync12, mkdirSync as mkdirSync13, copyFileSync, statSync as statSync14 } from "node:fs";
20388
+ import { join as join21, dirname as dirname10, resolve as resolve11, relative as relative7 } from "node:path";
20746
20389
  import { fileURLToPath as fileURLToPath5 } from "node:url";
20747
20390
  import { timingSafeEqual as timingSafeEqual2 } from "node:crypto";
20748
20391
  function escapeHtml(value) {
@@ -20861,7 +20504,7 @@ function readQueueDir(dir, topic, status2) {
20861
20504
  for (const filename of readdirSync12(dir).sort()) {
20862
20505
  if (!filename.endsWith(".queue-data")) continue;
20863
20506
  try {
20864
- jobs.push(mapQueueJob(JSON.parse(readFileSync19(join21(dir, filename), "utf-8")), topic, status2));
20507
+ jobs.push(mapQueueJob(JSON.parse(readFileSync18(join21(dir, filename), "utf-8")), topic, status2));
20865
20508
  } catch {
20866
20509
  }
20867
20510
  }
@@ -20980,7 +20623,7 @@ function resolveDevEnvVar(key) {
20980
20623
  if (live !== void 0 && live !== "") return live;
20981
20624
  const envPath = join21(process.cwd(), ".env");
20982
20625
  if (!existsSync19(envPath)) return "";
20983
- for (const line of readFileSync19(envPath, "utf-8").split("\n")) {
20626
+ for (const line of readFileSync18(envPath, "utf-8").split("\n")) {
20984
20627
  const t = line.trim();
20985
20628
  if (!t || t.startsWith("#") || !t.includes("=")) continue;
20986
20629
  const eq = t.indexOf("=");
@@ -20990,7 +20633,7 @@ function resolveDevEnvVar(key) {
20990
20633
  }
20991
20634
  function upsertDevEnvVar(key, value) {
20992
20635
  const envPath = join21(process.cwd(), ".env");
20993
- const lines = existsSync19(envPath) ? readFileSync19(envPath, "utf-8").split("\n") : [];
20636
+ const lines = existsSync19(envPath) ? readFileSync18(envPath, "utf-8").split("\n") : [];
20994
20637
  let found = false;
20995
20638
  const out = [];
20996
20639
  for (const line of lines) {
@@ -21023,7 +20666,7 @@ function parseEnvFile() {
21023
20666
  const envPath = join21(process.cwd(), ".env");
21024
20667
  const result = {};
21025
20668
  if (!existsSync19(envPath)) return result;
21026
- const lines = readFileSync19(envPath, "utf-8").split("\n");
20669
+ const lines = readFileSync18(envPath, "utf-8").split("\n");
21027
20670
  for (const line of lines) {
21028
20671
  const trimmed = line.trim();
21029
20672
  if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) continue;
@@ -21063,7 +20706,7 @@ function handleGalleryDeploy(router) {
21063
20706
  const copied = [];
21064
20707
  const allFiles = walkDirRecursive(gallerySrc);
21065
20708
  for (const srcFile of allFiles) {
21066
- const rel = relative8(gallerySrc, srcFile);
20709
+ const rel = relative7(gallerySrc, srcFile);
21067
20710
  const dest = join21(projectSrc, rel);
21068
20711
  mkdirSync13(dirname10(dest), { recursive: true });
21069
20712
  copyFileSync(srcFile, dest);
@@ -21725,9 +21368,6 @@ var init_devAdmin = __esm({
21725
21368
  { method: "GET", pattern: "/__dev/api/gallery", handler: handleGalleryList },
21726
21369
  { method: "POST", pattern: "/__dev/api/gallery/deploy", handler: handleGalleryDeploy(router) },
21727
21370
  // Metrics
21728
- { method: "GET", pattern: "/__dev/api/metrics", handler: (_req, res) => {
21729
- res.json(quickMetrics());
21730
- } },
21731
21371
  // No fallback (ADR-0002): a missing or stale CLI is a 503 naming the
21732
21372
  // install command, never zeros that read as a healthy codebase.
21733
21373
  { method: "GET", pattern: "/__dev/api/metrics/full", handler: (_req, res) => {
@@ -22536,7 +22176,7 @@ var init_devAdmin = __esm({
22536
22176
  }
22537
22177
  try {
22538
22178
  const envPath = join21(process.cwd(), ".env");
22539
- const lines = existsSync19(envPath) ? readFileSync19(envPath, "utf-8").split("\n") : [];
22179
+ const lines = existsSync19(envPath) ? readFileSync18(envPath, "utf-8").split("\n") : [];
22540
22180
  const keysFound = { TINA4_DATABASE_URL: false, TINA4_DATABASE_USERNAME: false, TINA4_DATABASE_PASSWORD: false };
22541
22181
  const newLines = [];
22542
22182
  for (const line of lines) {
@@ -22582,12 +22222,12 @@ var init_devAdmin = __esm({
22582
22222
  const metaFile = join21(entryPath, "meta.json");
22583
22223
  if (statSync14(entryPath).isDirectory() && existsSync19(metaFile)) {
22584
22224
  try {
22585
- const meta = JSON.parse(readFileSync19(metaFile, "utf-8"));
22225
+ const meta = JSON.parse(readFileSync18(metaFile, "utf-8"));
22586
22226
  meta.id = entry;
22587
22227
  const srcDir = join21(entryPath, "src");
22588
22228
  if (existsSync19(srcDir)) {
22589
22229
  const allFiles = walkDirRecursive(srcDir);
22590
- meta.files = allFiles.map((f) => relative8(srcDir, f));
22230
+ meta.files = allFiles.map((f) => relative7(srcDir, f));
22591
22231
  }
22592
22232
  const projectSrc = resolve11(process.cwd(), "src");
22593
22233
  if (existsSync19(srcDir) && meta.files) {
@@ -22705,7 +22345,7 @@ var init_devAdmin = __esm({
22705
22345
  for (const name of readdirSync12(target).sort()) {
22706
22346
  if (devFilesHidden(name)) continue;
22707
22347
  const full = join21(target, name);
22708
- const entryRel = relative8(root, full).replace(/\\/g, "/");
22348
+ const entryRel = relative7(root, full).replace(/\\/g, "/");
22709
22349
  if (isSecretPath(entryRel)) continue;
22710
22350
  let isDir = false;
22711
22351
  let size = null;
@@ -22750,7 +22390,7 @@ var init_devAdmin = __esm({
22750
22390
  size
22751
22391
  });
22752
22392
  }
22753
- res.json({ path: relative8(root, target).replace(/\\/g, "/") || ".", branch, entries });
22393
+ res.json({ path: relative7(root, target).replace(/\\/g, "/") || ".", branch, entries });
22754
22394
  };
22755
22395
  DEV_ADMIN_LANG_MAP = {
22756
22396
  ".py": "python",
@@ -22802,8 +22442,8 @@ var init_devAdmin = __esm({
22802
22442
  return;
22803
22443
  }
22804
22444
  try {
22805
- const content = readFileSync19(target, "utf-8");
22806
- const path8 = relative8(root, target);
22445
+ const content = readFileSync18(target, "utf-8");
22446
+ const path8 = relative7(root, target);
22807
22447
  res.json({ path: path8, content, language: devAdminLanguage(path8), bytes: Buffer.byteLength(content, "utf-8") });
22808
22448
  } catch (e) {
22809
22449
  res.json({ error: e.message }, 500);
@@ -22825,10 +22465,10 @@ var init_devAdmin = __esm({
22825
22465
  writeFileSync12(target, content, "utf-8");
22826
22466
  try {
22827
22467
  const { Plan: Plan2 } = await Promise.resolve().then(() => (init_plan(), plan_exports));
22828
- Plan2.recordAction(existed ? "patched" : "created", relative8(root, target));
22468
+ Plan2.recordAction(existed ? "patched" : "created", relative7(root, target));
22829
22469
  } catch {
22830
22470
  }
22831
- res.json({ ok: true, path: relative8(root, target), bytes: Buffer.byteLength(content, "utf-8") });
22471
+ res.json({ ok: true, path: relative7(root, target), bytes: Buffer.byteLength(content, "utf-8") });
22832
22472
  } catch (e) {
22833
22473
  res.json({ error: e.message }, 500);
22834
22474
  }
@@ -22848,7 +22488,7 @@ var init_devAdmin = __esm({
22848
22488
  return;
22849
22489
  }
22850
22490
  try {
22851
- const buf = readFileSync19(target);
22491
+ const buf = readFileSync18(target);
22852
22492
  const ext = target.slice(target.lastIndexOf(".") + 1).toLowerCase();
22853
22493
  const mime = {
22854
22494
  js: "application/javascript",
@@ -22890,7 +22530,7 @@ var init_devAdmin = __esm({
22890
22530
  const { renameSync: renameSync3 } = await import("node:fs");
22891
22531
  mkdirSync13(dirname10(dst), { recursive: true });
22892
22532
  renameSync3(src, dst);
22893
- res.json({ ok: true, from: relative8(root, src), to: relative8(root, dst) });
22533
+ res.json({ ok: true, from: relative7(root, src), to: relative7(root, dst) });
22894
22534
  } catch (e) {
22895
22535
  res.json({ error: e.message }, 500);
22896
22536
  }
@@ -22911,7 +22551,7 @@ var init_devAdmin = __esm({
22911
22551
  try {
22912
22552
  const { rmSync } = await import("node:fs");
22913
22553
  rmSync(target, { recursive: true, force: true });
22914
- res.json({ ok: true, deleted: relative8(root, target) });
22554
+ res.json({ ok: true, deleted: relative7(root, target) });
22915
22555
  } catch (e) {
22916
22556
  res.json({ error: e.message }, 500);
22917
22557
  }
@@ -23245,7 +22885,7 @@ var init_devAdmin = __esm({
23245
22885
  });
23246
22886
  };
23247
22887
  handleDevAdminJs = async (_req, res) => {
23248
- const { readFileSync: readFileSync28, existsSync: existsSync27 } = await import("node:fs");
22888
+ const { readFileSync: readFileSync27, existsSync: existsSync27 } = await import("node:fs");
23249
22889
  const { dirname: dirname15, join: join32, resolve: resolve20 } = await import("node:path");
23250
22890
  const { fileURLToPath: fileURLToPath8 } = await import("node:url");
23251
22891
  const dir = dirname15(fileURLToPath8(import.meta.url));
@@ -23261,7 +22901,7 @@ var init_devAdmin = __esm({
23261
22901
  for (const jsPath of candidates) {
23262
22902
  if (existsSync27(jsPath)) {
23263
22903
  try {
23264
- const content = readFileSync28(jsPath, "utf-8");
22904
+ const content = readFileSync27(jsPath, "utf-8");
23265
22905
  res.raw.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8", "Cache-Control": "no-cache" });
23266
22906
  res.raw.end(content);
23267
22907
  return;
@@ -23276,7 +22916,7 @@ var init_devAdmin = __esm({
23276
22916
  });
23277
22917
 
23278
22918
  // ../core/src/i18n.ts
23279
- import { readFileSync as readFileSync20, readdirSync as readdirSync13, existsSync as existsSync20 } from "node:fs";
22919
+ import { readFileSync as readFileSync19, readdirSync as readdirSync13, existsSync as existsSync20 } from "node:fs";
23280
22920
  import { join as join22, resolve as resolve12 } from "node:path";
23281
22921
  var I18n;
23282
22922
  var init_i18n = __esm({
@@ -23373,7 +23013,7 @@ var init_i18n = __esm({
23373
23013
  const filePath = join22(this._localeDir, `${locale}.json`);
23374
23014
  if (existsSync20(filePath)) {
23375
23015
  try {
23376
- const raw = readFileSync20(filePath, "utf-8");
23016
+ const raw = readFileSync19(filePath, "utf-8");
23377
23017
  const data = JSON.parse(raw);
23378
23018
  this._translations.set(locale, _I18n._flatten(data));
23379
23019
  return;
@@ -23386,7 +23026,7 @@ var init_i18n = __esm({
23386
23026
  const yamlPath = join22(this._localeDir, `${locale}${ext}`);
23387
23027
  if (existsSync20(yamlPath)) {
23388
23028
  try {
23389
- const raw = readFileSync20(yamlPath, "utf-8");
23029
+ const raw = readFileSync19(yamlPath, "utf-8");
23390
23030
  const data = _I18n._parseSimpleYaml(raw);
23391
23031
  this._translations.set(locale, _I18n._flatten(data));
23392
23032
  return;
@@ -24249,8 +23889,8 @@ var init_docsAutoDiscovery = __esm({
24249
23889
  // ../core/src/server.ts
24250
23890
  import { createServer as createServer2 } from "node:http";
24251
23891
  import { randomBytes as randomBytes5 } from "node:crypto";
24252
- import { resolve as resolve14, dirname as dirname11, join as join24, relative as relative9 } from "node:path";
24253
- import { existsSync as existsSync22, readdirSync as readdirSync14, readFileSync as readFileSync22, statSync as statSync15 } from "node:fs";
23892
+ import { resolve as resolve14, dirname as dirname11, join as join24, relative as relative8 } from "node:path";
23893
+ import { existsSync as existsSync22, readdirSync as readdirSync14, readFileSync as readFileSync21, statSync as statSync15 } from "node:fs";
24254
23894
  import { isatty } from "node:tty";
24255
23895
  import { fileURLToPath as fileURLToPath6 } from "node:url";
24256
23896
  import { execFileSync as execFileSync3, exec } from "node:child_process";
@@ -24474,7 +24114,7 @@ function getGalleryDeployedState() {
24474
24114
  if (existsSync22(srcDir)) {
24475
24115
  const files = walkGalleryFiles(srcDir);
24476
24116
  const projectSrc = resolve14(process.cwd(), "src");
24477
- state[entry] = files.every((f) => existsSync22(join24(projectSrc, relative9(srcDir, f))));
24117
+ state[entry] = files.every((f) => existsSync22(join24(projectSrc, relative8(srcDir, f))));
24478
24118
  } else {
24479
24119
  state[entry] = false;
24480
24120
  }
@@ -24911,7 +24551,7 @@ function serveTemplateFallback(ctx) {
24911
24551
  if ((ctx.req.method ?? "GET") !== "GET") return false;
24912
24552
  const tplFile = resolveTemplate(ctx.pathname, ctx.templatesDir);
24913
24553
  if (!tplFile) return false;
24914
- const html = ctx.frondEngine ? ctx.frondEngine.render(tplFile, {}) : readFileSync22(resolve14(ctx.templatesDir, tplFile), "utf-8");
24554
+ const html = ctx.frondEngine ? ctx.frondEngine.render(tplFile, {}) : readFileSync21(resolve14(ctx.templatesDir, tplFile), "utf-8");
24915
24555
  ctx.res.raw.writeHead(200, void 0, { "Content-Type": "text/html; charset=utf-8" });
24916
24556
  ctx.res.raw.end(html);
24917
24557
  return true;
@@ -26206,7 +25846,7 @@ var init_mqttMessage = __esm({
26206
25846
  import net2 from "node:net";
26207
25847
  import tls from "node:tls";
26208
25848
  import { randomBytes as randomBytes6 } from "node:crypto";
26209
- import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
25849
+ import { existsSync as existsSync24, readFileSync as readFileSync22 } from "node:fs";
26210
25850
  var MqttError, MqttTimeoutError, CONNECT, CONNACK, PUBLISH, PUBACK, SUBSCRIBE, SUBACK, PINGREQ, PINGRESP, DISCONNECT, PROTOCOL_LEVEL, DEFAULT_PORT, DEFAULT_TLS_PORT, DEFAULT_URL, DEFAULT_KEEPALIVE, SUBSCRIPTION_REFUSED, MAX_REMAINING_LENGTH, QOS2_REFUSED_MESSAGE, CONNACK_RETURN_CODES, Mqtt;
26211
25851
  var init_mqtt = __esm({
26212
25852
  "../core/src/mqtt.ts"() {
@@ -26671,7 +26311,7 @@ var init_mqtt = __esm({
26671
26311
  servername: this.host,
26672
26312
  rejectUnauthorized: this.tlsVerify
26673
26313
  };
26674
- if (this.tlsVerify && this.caFile) opts.ca = readFileSync23(this.caFile);
26314
+ if (this.tlsVerify && this.caFile) opts.ca = readFileSync22(this.caFile);
26675
26315
  sock = tls.connect(opts, () => settle(() => resolve20(sock)));
26676
26316
  } else {
26677
26317
  sock = net2.createConnection({ host: this.host, port: this.port }, () => settle(() => resolve20(sock)));
@@ -26892,7 +26532,7 @@ var init_mqtt = __esm({
26892
26532
 
26893
26533
  // ../core/src/service.ts
26894
26534
  import { readdirSync as readdirSync16, statSync as statSync16, watchFile, unwatchFile } from "node:fs";
26895
- import { join as join26, extname as extname7 } from "node:path";
26535
+ import { join as join26, extname as extname6 } from "node:path";
26896
26536
  import { pathToFileURL } from "node:url";
26897
26537
  function matchCronField(field, value) {
26898
26538
  if (field === "*") return true;
@@ -27063,7 +26703,7 @@ var init_service = __esm({
27063
26703
  return discovered;
27064
26704
  }
27065
26705
  for (const entry of entries) {
27066
- const ext = extname7(entry);
26706
+ const ext = extname6(entry);
27067
26707
  if (ext !== ".ts" && ext !== ".js") continue;
27068
26708
  const fullPath = join26(dir, entry);
27069
26709
  const stat = statSync16(fullPath);
@@ -27179,7 +26819,7 @@ var init_service = __esm({
27179
26819
  return;
27180
26820
  }
27181
26821
  for (const entry of entries) {
27182
- const ext = extname7(entry);
26822
+ const ext = extname6(entry);
27183
26823
  if (ext !== ".ts" && ext !== ".js") continue;
27184
26824
  const fullPath = join26(dir, entry);
27185
26825
  if (watchedFiles.has(fullPath)) continue;
@@ -27882,7 +27522,7 @@ var init_api = __esm({
27882
27522
  // ../core/src/messenger.ts
27883
27523
  import net3 from "node:net";
27884
27524
  import tls2 from "node:tls";
27885
- import { readFileSync as readFileSync24 } from "node:fs";
27525
+ import { readFileSync as readFileSync23 } from "node:fs";
27886
27526
  import { basename as basename6 } from "node:path";
27887
27527
  import { randomUUID as randomUUID7 } from "node:crypto";
27888
27528
  function tlsRejectUnauthorized() {
@@ -27979,7 +27619,7 @@ function buildMimeMessage(options) {
27979
27619
  }
27980
27620
  for (const filePath of options.attachments) {
27981
27621
  const fileName = basename6(filePath);
27982
- const fileData = readFileSync24(filePath);
27622
+ const fileData = readFileSync23(filePath);
27983
27623
  const base64Data = fileData.toString("base64");
27984
27624
  lines.push("");
27985
27625
  lines.push(`--${boundary}`);
@@ -29442,9 +29082,9 @@ var init_htmlElement = __esm({
29442
29082
  });
29443
29083
 
29444
29084
  // ../core/src/ai.ts
29445
- import { existsSync as existsSync25, mkdirSync as mkdirSync16, writeFileSync as writeFileSync15, readFileSync as readFileSync25 } from "node:fs";
29085
+ import { existsSync as existsSync25, mkdirSync as mkdirSync16, writeFileSync as writeFileSync15, readFileSync as readFileSync24 } from "node:fs";
29446
29086
  import { homedir } from "node:os";
29447
- import { join as join27, resolve as resolve16, relative as relative10, dirname as dirname12 } from "node:path";
29087
+ import { join as join27, resolve as resolve16, relative as relative9, dirname as dirname12 } from "node:path";
29448
29088
  import { fileURLToPath as fileURLToPath7 } from "node:url";
29449
29089
  import { execSync, execFileSync as execFileSync4 } from "node:child_process";
29450
29090
  import { createInterface } from "node:readline";
@@ -29452,7 +29092,7 @@ function readVersion() {
29452
29092
  try {
29453
29093
  const thisDir = dirname12(fileURLToPath7(import.meta.url));
29454
29094
  const rootPkg = resolve16(thisDir, "..", "..", "..", "package.json");
29455
- const pkg = JSON.parse(readFileSync25(rootPkg, "utf-8"));
29095
+ const pkg = JSON.parse(readFileSync24(rootPkg, "utf-8"));
29456
29096
  return pkg.version ?? "0.0.0";
29457
29097
  } catch {
29458
29098
  return "0.0.0";
@@ -29675,7 +29315,7 @@ function writeOrMerge(contextPath, contextFile, frameworkGuide) {
29675
29315
  writeFileSync15(contextPath, frameworkGuide.replace(/\s+$/, "") + "\n\n" + block + "\n", "utf-8");
29676
29316
  return "Installed";
29677
29317
  }
29678
- const existing = readFileSync25(contextPath, "utf-8");
29318
+ const existing = readFileSync24(contextPath, "utf-8");
29679
29319
  if (hasMarkers(existing, start2, end)) {
29680
29320
  writeFileSync15(contextPath, replaceMarkerBlock(existing, block, start2, end), "utf-8");
29681
29321
  return "Refreshed skill block in";
@@ -29699,7 +29339,7 @@ function installForTool(root, tool, context) {
29699
29339
  const parentDir = dirname12(contextPath);
29700
29340
  mkdirSync16(parentDir, { recursive: true });
29701
29341
  const action = writeOrMerge(contextPath, tool.contextFile, context);
29702
- const rel = relative10(root, contextPath);
29342
+ const rel = relative9(root, contextPath);
29703
29343
  created.push(rel);
29704
29344
  console.log(` ${GREEN2}\u2713${RESET2} ${action} ${rel}`);
29705
29345
  if (tool.name === "claude-code") {
@@ -30077,7 +29717,7 @@ function generateClaudeCodeContext() {
30077
29717
  const repoRoot = resolve16(thisDir, "..", "..", "..");
30078
29718
  const claudeMdPath = join27(repoRoot, "CLAUDE.md");
30079
29719
  if (existsSync25(claudeMdPath)) {
30080
- return readFileSync25(claudeMdPath, "utf-8");
29720
+ return readFileSync24(claudeMdPath, "utf-8");
30081
29721
  }
30082
29722
  } catch {
30083
29723
  }
@@ -30271,6 +29911,292 @@ export default class User {
30271
29911
  }
30272
29912
  });
30273
29913
 
29914
+ // ../core/src/aiClient.ts
29915
+ import http2 from "node:http";
29916
+ import https2 from "node:https";
29917
+ var AiError, AiConfigError, AiTimeoutError, AiParseError, AiHTTPError, Ai;
29918
+ var init_aiClient = __esm({
29919
+ "../core/src/aiClient.ts"() {
29920
+ "use strict";
29921
+ AiError = class extends Error {
29922
+ };
29923
+ AiConfigError = class extends AiError {
29924
+ };
29925
+ AiTimeoutError = class extends AiError {
29926
+ };
29927
+ AiParseError = class extends AiError {
29928
+ };
29929
+ AiHTTPError = class extends AiError {
29930
+ constructor(message, status2 = null) {
29931
+ super(message);
29932
+ this.status = status2;
29933
+ }
29934
+ };
29935
+ Ai = class {
29936
+ static chat(messages, options = {}) {
29937
+ this.validateMessages(messages);
29938
+ const config = this.config("chat", options);
29939
+ const body = this.chatBody(config, messages, options);
29940
+ const headers = this.headers(config);
29941
+ return options.stream ? this.streamRequest(config, headers, body) : this.chatResponse(config, headers, body);
29942
+ }
29943
+ static async complete(prompt, options = {}) {
29944
+ if (typeof prompt !== "string") throw new AiConfigError("AI prompt must be a string");
29945
+ return (await this.chat([{ role: "user", content: prompt }], { ...options, stream: false })).text;
29946
+ }
29947
+ static async embed(textOrTexts, options = {}) {
29948
+ const single = typeof textOrTexts === "string";
29949
+ if (!single && (!Array.isArray(textOrTexts) || textOrTexts.length === 0 || !textOrTexts.every((item) => typeof item === "string"))) {
29950
+ throw new AiConfigError("AI embedding input must be a string or a non-empty list of strings");
29951
+ }
29952
+ const config = this.config("embed", options);
29953
+ if (config.provider === "anthropic") throw new AiConfigError("Anthropic does not provide the embedding endpoint in this contract");
29954
+ const raw = await this.requestJson(config, this.headers(config), { model: config.model, input: textOrTexts });
29955
+ try {
29956
+ const data = raw.data.sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
29957
+ const vectors = data.map((item) => item.embedding);
29958
+ const expected = single ? 1 : textOrTexts.length;
29959
+ if (vectors.length !== expected || !vectors.every((vector) => Array.isArray(vector) && vector.length > 0 && vector.every((value) => typeof value === "number" && Number.isFinite(value)))) throw new Error();
29960
+ return single ? vectors[0] : vectors;
29961
+ } catch {
29962
+ throw new AiParseError("AI provider returned a malformed embedding response");
29963
+ }
29964
+ }
29965
+ static validateMessages(messages) {
29966
+ if (!Array.isArray(messages) || messages.length === 0 || !messages.every((message) => message && ["system", "user", "assistant"].includes(message.role) && typeof message.content === "string")) {
29967
+ throw new AiConfigError("AI messages must contain supported roles and string content");
29968
+ }
29969
+ }
29970
+ static number(name, fallback, minimum) {
29971
+ const value = process.env[name] === void 0 ? fallback : Number(process.env[name]);
29972
+ if (!Number.isFinite(value) || value < minimum) throw new AiConfigError(`${name} must be numeric and at least ${minimum}`);
29973
+ return value;
29974
+ }
29975
+ static config(capability, options) {
29976
+ const provider = (options.provider ?? process.env.TINA4_AI_PROVIDER ?? "local").trim().toLowerCase();
29977
+ if (provider !== "local" && provider !== "openai" && provider !== "anthropic") throw new AiConfigError("TINA4_AI_PROVIDER must be local, openai, or anthropic");
29978
+ const key = process.env.TINA4_AI_KEY || null;
29979
+ if ((provider === "openai" || provider === "anthropic") && !key) throw new AiConfigError(`TINA4_AI_KEY is required for the ${provider} provider`);
29980
+ const defaults = {
29981
+ local: ["http://localhost:11437", "llama3.2"],
29982
+ openai: ["https://api.openai.com/v1", "gpt-4o-mini"],
29983
+ anthropic: ["https://api.anthropic.com/v1", "claude-3-5-haiku-latest"]
29984
+ };
29985
+ const rawUrl = capability === "embed" && process.env.TINA4_EMBED_URL ? process.env.TINA4_EMBED_URL : process.env.TINA4_AI_URL ?? defaults[provider][0];
29986
+ const model = (options.model ?? process.env.TINA4_AI_MODEL ?? defaults[provider][1]).trim();
29987
+ if (!model) throw new AiConfigError("AI model must be a non-empty string");
29988
+ const totalTimeout = options.timeout === void 0 ? this.number("TINA4_AI_TIMEOUT", 60, 1e-3) : Number(options.timeout);
29989
+ if (!Number.isFinite(totalTimeout) || totalTimeout <= 0) throw new AiConfigError("AI timeout must be greater than zero");
29990
+ return { provider, url: this.endpoint(rawUrl, capability, provider), model, key, totalTimeout, connectTimeout: this.number("TINA4_AI_CONNECT_TIMEOUT", 10, 1e-3), maxRetries: Math.trunc(this.number("TINA4_AI_MAX_RETRIES", 2, 0)) };
29991
+ }
29992
+ static endpoint(value, capability, provider) {
29993
+ let url;
29994
+ try {
29995
+ url = new URL(value);
29996
+ } catch {
29997
+ throw new AiConfigError("AI URL must be an http or https URL");
29998
+ }
29999
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new AiConfigError("AI URL must be an http or https URL");
30000
+ const path8 = url.pathname.replace(/\/+$/, "");
30001
+ if (path8 === "" || path8 === "/v1" || path8 === "/api") {
30002
+ const suffix = provider === "anthropic" ? "/messages" : capability === "embed" ? "/embeddings" : "/chat/completions";
30003
+ url.pathname = (path8 || "/v1") + suffix;
30004
+ }
30005
+ return url.toString();
30006
+ }
30007
+ static headers(config) {
30008
+ const headers = { "content-type": "application/json", accept: "application/json" };
30009
+ if (config.provider === "openai") headers.authorization = `Bearer ${config.key}`;
30010
+ if (config.provider === "anthropic") {
30011
+ headers["x-api-key"] = config.key;
30012
+ headers["anthropic-version"] = "2023-06-01";
30013
+ }
30014
+ return headers;
30015
+ }
30016
+ static chatBody(config, messages, options) {
30017
+ const body = { model: config.model, messages, stream: options.stream ?? false };
30018
+ if (options.temperature !== void 0) body.temperature = options.temperature;
30019
+ if (options.maxTokens !== void 0) body.max_tokens = options.maxTokens;
30020
+ if (config.provider === "anthropic") {
30021
+ const system = messages.filter((message) => message.role === "system").map((message) => message.content);
30022
+ body.messages = messages.filter((message) => message.role !== "system");
30023
+ body.max_tokens = options.maxTokens ?? 1024;
30024
+ if (system.length) body.system = system.join("\n\n");
30025
+ }
30026
+ return body;
30027
+ }
30028
+ static open(config, deadline, headers, body) {
30029
+ const remainingMs = deadline - performance.now();
30030
+ if (remainingMs <= 0) return Promise.reject(new AiTimeoutError("AI total request timeout expired"));
30031
+ const url = new URL(config.url);
30032
+ const payload = JSON.stringify(body);
30033
+ const controller = new AbortController();
30034
+ const totalTimer = setTimeout(() => controller.abort(new AiTimeoutError("AI total request timeout expired")), remainingMs);
30035
+ return new Promise((resolve20, reject) => {
30036
+ const client = url.protocol === "https:" ? https2 : http2;
30037
+ const request = client.request(url, { method: "POST", headers: { ...headers, "content-length": Buffer.byteLength(payload) }, signal: controller.signal }, (response) => {
30038
+ clearTimeout(connectTimer);
30039
+ resolve20({ response, cleanup: () => {
30040
+ clearTimeout(totalTimer);
30041
+ clearTimeout(connectTimer);
30042
+ } });
30043
+ });
30044
+ const connectTimer = setTimeout(() => request.destroy(new AiTimeoutError("AI connection timeout expired")), Math.min(config.connectTimeout * 1e3, remainingMs));
30045
+ request.on("socket", (socket) => {
30046
+ if (!socket.connecting) clearTimeout(connectTimer);
30047
+ socket.once(url.protocol === "https:" ? "secureConnect" : "connect", () => clearTimeout(connectTimer));
30048
+ });
30049
+ request.once("error", (error) => {
30050
+ clearTimeout(totalTimer);
30051
+ clearTimeout(connectTimer);
30052
+ if (error instanceof AiError) reject(error);
30053
+ else if (controller.signal.aborted) reject(new AiTimeoutError("AI total request timeout expired"));
30054
+ else reject(new AiHTTPError(`AI transport failed (${error.name})`));
30055
+ });
30056
+ request.end(payload);
30057
+ });
30058
+ }
30059
+ static async readBody(response) {
30060
+ const chunks = [];
30061
+ for await (const chunk of response) chunks.push(Buffer.from(chunk));
30062
+ return Buffer.concat(chunks).toString("utf8");
30063
+ }
30064
+ static retryDelay(headers, deadline) {
30065
+ const value = Array.isArray(headers["retry-after"]) ? headers["retry-after"][0] : headers["retry-after"];
30066
+ const requested = value !== void 0 && Number.isFinite(Number(value)) ? Math.max(0, Number(value) * 1e3) : 100;
30067
+ const delay = Math.min(requested, Math.max(0, deadline - performance.now()));
30068
+ return new Promise((resolve20) => setTimeout(resolve20, delay));
30069
+ }
30070
+ static async requestJson(config, headers, body) {
30071
+ const deadline = performance.now() + config.totalTimeout * 1e3;
30072
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
30073
+ let opened = null;
30074
+ try {
30075
+ opened = await this.open(config, deadline, headers, body);
30076
+ const status2 = opened.response.statusCode ?? 0;
30077
+ const responseHeaders = opened.response.headers;
30078
+ const raw = await this.readBody(opened.response);
30079
+ opened.cleanup();
30080
+ opened = null;
30081
+ if (status2 < 200 || status2 >= 300) {
30082
+ if ((status2 === 429 || status2 >= 500) && attempt < config.maxRetries) {
30083
+ await this.retryDelay(responseHeaders, deadline);
30084
+ continue;
30085
+ }
30086
+ throw new AiHTTPError(`AI provider returned HTTP ${status2}`, status2);
30087
+ }
30088
+ let parsed;
30089
+ try {
30090
+ parsed = JSON.parse(raw);
30091
+ } catch {
30092
+ throw new AiParseError("AI provider returned malformed JSON");
30093
+ }
30094
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new AiParseError("AI provider returned a non-object JSON response");
30095
+ return parsed;
30096
+ } catch (error) {
30097
+ opened?.cleanup();
30098
+ if (error instanceof AiParseError || error instanceof AiHTTPError && error.status !== null) throw error;
30099
+ if (attempt >= config.maxRetries) throw error;
30100
+ }
30101
+ }
30102
+ throw new AiHTTPError("AI request failed");
30103
+ }
30104
+ static normalizeChat(provider, raw) {
30105
+ try {
30106
+ if (provider === "anthropic") {
30107
+ const content = raw.content;
30108
+ const parts = content.filter((item) => (item.type ?? "text") === "text").map((item) => item.text);
30109
+ if (!parts.length || !parts.every((part) => typeof part === "string")) throw new Error();
30110
+ const usage2 = raw.usage ?? {};
30111
+ const promptTokens = Number(usage2.input_tokens ?? 0);
30112
+ const completionTokens = Number(usage2.output_tokens ?? 0);
30113
+ return { text: parts.join(""), model: String(raw.model ?? ""), usage: { promptTokens, completionTokens, totalTokens: promptTokens + completionTokens }, finishReason: raw.stop_reason == null ? null : String(raw.stop_reason), raw };
30114
+ }
30115
+ const choice = raw.choices[0];
30116
+ const text = choice.message.content;
30117
+ if (typeof text !== "string") throw new Error();
30118
+ const usage = raw.usage ?? {};
30119
+ return { text, model: String(raw.model ?? ""), usage: { promptTokens: Number(usage.prompt_tokens ?? 0), completionTokens: Number(usage.completion_tokens ?? 0), totalTokens: Number(usage.total_tokens ?? 0) }, finishReason: choice.finish_reason == null ? null : String(choice.finish_reason), raw };
30120
+ } catch {
30121
+ throw new AiParseError("AI provider returned a malformed chat response");
30122
+ }
30123
+ }
30124
+ static async chatResponse(config, headers, body) {
30125
+ return this.normalizeChat(config.provider, await this.requestJson(config, headers, body));
30126
+ }
30127
+ static streamDelta(provider, data) {
30128
+ if (data === "[DONE]") return { completed: true };
30129
+ let event;
30130
+ try {
30131
+ event = JSON.parse(data);
30132
+ } catch {
30133
+ throw new AiParseError("AI provider returned malformed stream data");
30134
+ }
30135
+ const text = provider === "anthropic" ? event.type === "content_block_delta" ? event.delta?.text : void 0 : event.choices?.[0]?.delta?.content;
30136
+ if (text !== void 0 && text !== null && typeof text !== "string") throw new AiParseError("AI provider returned malformed stream data");
30137
+ return { completed: false, text };
30138
+ }
30139
+ static async *streamData(response) {
30140
+ let buffer = "";
30141
+ for await (const chunk of response) {
30142
+ buffer += Buffer.from(chunk).toString("utf8");
30143
+ let newline;
30144
+ while ((newline = buffer.indexOf("\n")) >= 0) {
30145
+ const line = buffer.slice(0, newline).trim();
30146
+ buffer = buffer.slice(newline + 1);
30147
+ if (line.startsWith("data:")) yield line.slice(5).trim();
30148
+ }
30149
+ }
30150
+ }
30151
+ static streamError(error) {
30152
+ if (error instanceof AiError) return error;
30153
+ if (error instanceof Error && error.name === "AbortError") return new AiTimeoutError("AI total request timeout expired");
30154
+ return new AiHTTPError(`AI transport failed (${error instanceof Error ? error.name : "Error"})`);
30155
+ }
30156
+ static async *streamRequest(config, headers, body) {
30157
+ const deadline = performance.now() + config.totalTimeout * 1e3;
30158
+ let yielded = false;
30159
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
30160
+ let opened = null;
30161
+ try {
30162
+ opened = await this.open(config, deadline, { ...headers, accept: "text/event-stream" }, body);
30163
+ const status2 = opened.response.statusCode ?? 0;
30164
+ if (status2 < 200 || status2 >= 300) {
30165
+ await this.readBody(opened.response);
30166
+ if ((status2 === 429 || status2 >= 500) && attempt < config.maxRetries) {
30167
+ await this.retryDelay(opened.response.headers, deadline);
30168
+ opened.cleanup();
30169
+ opened = null;
30170
+ continue;
30171
+ }
30172
+ throw new AiHTTPError(`AI provider returned HTTP ${status2}`, status2);
30173
+ }
30174
+ let completed = false;
30175
+ for await (const data of this.streamData(opened.response)) {
30176
+ const delta = this.streamDelta(config.provider, data);
30177
+ if (delta.completed) {
30178
+ completed = true;
30179
+ break;
30180
+ }
30181
+ if (delta.text === void 0) continue;
30182
+ yielded = true;
30183
+ yield delta.text;
30184
+ }
30185
+ opened.cleanup();
30186
+ opened = null;
30187
+ if (completed) return;
30188
+ throw new AiParseError("AI provider stream ended before [DONE]");
30189
+ } catch (error) {
30190
+ opened?.cleanup();
30191
+ const failure = this.streamError(error);
30192
+ if (failure instanceof AiParseError || failure instanceof AiHTTPError && failure.status !== null || yielded || attempt >= config.maxRetries) throw failure;
30193
+ }
30194
+ }
30195
+ }
30196
+ };
30197
+ }
30198
+ });
30199
+
30274
30200
  // ../core/src/queueBackends/rabbitmqBackend.ts
30275
30201
  import { execFileSync as execFileSync5 } from "node:child_process";
30276
30202
  import { randomUUID as randomUUID8 } from "node:crypto";
@@ -31974,6 +31900,12 @@ __export(src_exports2, {
31974
31900
  APPLICATION_JSON: () => APPLICATION_JSON,
31975
31901
  APPLICATION_OCTET: () => APPLICATION_OCTET,
31976
31902
  APPLICATION_XML: () => APPLICATION_XML,
31903
+ Ai: () => Ai,
31904
+ AiConfigError: () => AiConfigError,
31905
+ AiError: () => AiError,
31906
+ AiHTTPError: () => AiHTTPError,
31907
+ AiParseError: () => AiParseError,
31908
+ AiTimeoutError: () => AiTimeoutError,
31977
31909
  Api: () => Api,
31978
31910
  Auth: () => Auth,
31979
31911
  CANONICAL_SESSION_BACKENDS: () => CANONICAL_SESSION_BACKENDS,
@@ -32301,6 +32233,7 @@ var init_src2 = __esm({
32301
32233
  init_htmlElement();
32302
32234
  init_errorOverlay();
32303
32235
  init_ai();
32236
+ init_aiClient();
32304
32237
  init_liteBackend();
32305
32238
  init_rabbitmqBackend();
32306
32239
  init_kafkaBackend();
@@ -37739,7 +37672,7 @@ var init_database = __esm({
37739
37672
 
37740
37673
  // src/model.ts
37741
37674
  import { readdirSync as readdirSync17, statSync as statSync17 } from "node:fs";
37742
- import { join as join29, extname as extname8 } from "node:path";
37675
+ import { join as join29, extname as extname7 } from "node:path";
37743
37676
  async function discoverModels(modelsDir) {
37744
37677
  const models = [];
37745
37678
  let files;
@@ -37752,7 +37685,7 @@ async function discoverModels(modelsDir) {
37752
37685
  const filePath = join29(modelsDir, file);
37753
37686
  const stat = statSync17(filePath);
37754
37687
  if (!stat.isFile()) continue;
37755
- const ext = extname8(file);
37688
+ const ext = extname7(file);
37756
37689
  if (ext !== ".ts" && ext !== ".js") continue;
37757
37690
  try {
37758
37691
  const moduleUrl = `file://${filePath}?t=${Date.now()}`;
@@ -37791,7 +37724,7 @@ var init_model = __esm({
37791
37724
  });
37792
37725
 
37793
37726
  // src/migration.ts
37794
- import { existsSync as existsSync26, readdirSync as readdirSync18, readFileSync as readFileSync26, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "node:fs";
37727
+ import { existsSync as existsSync26, readdirSync as readdirSync18, readFileSync as readFileSync25, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "node:fs";
37795
37728
  import { join as join30, resolve as resolve18 } from "node:path";
37796
37729
  function unwrapAdapter(db) {
37797
37730
  let cur = db;
@@ -38119,7 +38052,7 @@ async function rollback(migrationsDir, delimiter2) {
38119
38052
  `Cannot rollback ${migration.migration_name}: no .down.sql file found`
38120
38053
  );
38121
38054
  }
38122
- const sqlContent = readFileSync26(downPath, "utf-8").trim();
38055
+ const sqlContent = readFileSync25(downPath, "utf-8").trim();
38123
38056
  if (sqlContent) {
38124
38057
  const statements = splitStatements(sqlContent, delim);
38125
38058
  try {
@@ -38317,7 +38250,7 @@ async function migrate(adapter, options) {
38317
38250
  result.skipped.push(file);
38318
38251
  continue;
38319
38252
  }
38320
- const sqlContent = readFileSync26(join30(dir, file), "utf-8").trim();
38253
+ const sqlContent = readFileSync25(join30(dir, file), "utf-8").trim();
38321
38254
  if (!sqlContent) {
38322
38255
  result.skipped.push(file);
38323
38256
  continue;
@@ -42008,7 +41941,7 @@ var init_attachment = __esm({
42008
41941
 
42009
41942
  // src/realtime/storage.ts
42010
41943
  import { randomBytes as randomBytes9 } from "node:crypto";
42011
- import { mkdirSync as mkdirSync20, readFileSync as readFileSync27, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, statSync as statSync18 } from "node:fs";
41944
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync26, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, statSync as statSync18 } from "node:fs";
42012
41945
  import { resolve as resolve19, sep as sep5 } from "node:path";
42013
41946
  import { createRequire as createRequire8 } from "node:module";
42014
41947
  function storageKey(filename = "") {
@@ -42061,7 +41994,7 @@ var init_storage = __esm({
42061
41994
  }
42062
41995
  get(key) {
42063
41996
  try {
42064
- return readFileSync27(this.pathFor(key));
41997
+ return readFileSync26(this.pathFor(key));
42065
41998
  } catch {
42066
41999
  return null;
42067
42000
  }