tina4-nodejs 3.13.149 → 3.13.151
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/CLAUDE.md +2 -2
- package/package.json +5 -9
- package/packages/cli/dist/bin.js +1074 -521
- package/packages/core/dist/index.js +1071 -516
- package/packages/core/public/bundled-component-provenance.json +2 -2
- package/packages/core/public/js/tina4-dev-admin-split.html +202 -0
- package/packages/core/public/js/tina4-dev-admin.min.js +147 -138
- package/packages/core/src/aiClient.ts +2 -1
- package/packages/core/src/api.ts +2 -1
- package/packages/core/src/auth.ts +1 -1
- package/packages/core/src/cache.ts +6 -1
- package/packages/core/src/devAdmin.ts +209 -123
- package/packages/core/src/dispatchPipeline.ts +4 -0
- package/packages/core/src/docs.ts +5 -3
- package/packages/core/src/errorOverlay.ts +18 -9
- package/packages/core/src/index.ts +2 -1
- package/packages/core/src/mcp.ts +53 -16
- package/packages/core/src/messenger.ts +22 -30
- package/packages/core/src/metrics.ts +5 -1
- package/packages/core/src/middleware.ts +8 -1
- package/packages/core/src/optionalPackage.ts +2 -2
- package/packages/core/src/plan.ts +4 -4
- package/packages/core/src/push.ts +1 -1
- package/packages/core/src/request.ts +185 -25
- package/packages/core/src/router.ts +3 -2
- package/packages/core/src/server.ts +32 -10
- package/packages/core/src/session.ts +44 -0
- package/packages/core/src/slashTrim.ts +39 -0
- package/packages/core/src/wsdl.ts +66 -48
- package/packages/core/templates/crud/form.twig +5 -5
- package/packages/core/templates/crud/table.twig +2 -2
- package/packages/core/templates/errors/302.twig +4 -3
- package/packages/frond/dist/index.js +206 -64
- package/packages/frond/src/engine.ts +278 -69
- package/packages/frond/src/index.ts +1 -1
- package/packages/orm/dist/index.js +1055 -502
- package/packages/orm/src/crudRender.ts +5 -5
- package/packages/orm/src/crudTable.ts +1 -1
- package/packages/orm/src/database.ts +15 -29
- package/packages/orm/src/databaseResult.ts +11 -1
- package/packages/orm/src/realtime/index.ts +1 -0
- package/packages/orm/src/realtime/realtime.ts +2 -1
- package/packages/orm/src/realtime/s3Client.ts +276 -0
- package/packages/orm/src/realtime/storage.ts +19 -60
- package/packages/orm/src/sqlTranslator.ts +58 -42
- package/packages/swagger/dist/index.js +25 -4
- package/packages/swagger/src/generator.ts +7 -4
- package/packages/swagger/src/ui.ts +2 -1
- package/types/core/src/devAdmin.d.ts +40 -0
- package/types/core/src/index.d.ts +2 -1
- package/types/core/src/middleware.d.ts +1 -0
- package/types/core/src/optionalPackage.d.ts +2 -2
- package/types/core/src/request.d.ts +5 -1
- package/types/core/src/session.d.ts +25 -0
- package/types/core/src/slashTrim.d.ts +6 -0
- package/types/frond/src/engine.d.ts +11 -0
- package/types/frond/src/index.d.ts +1 -1
- package/types/orm/src/realtime/index.d.ts +1 -0
- package/types/orm/src/realtime/s3Client.d.ts +48 -0
- package/types/orm/src/realtime/storage.d.ts +9 -7
|
@@ -51,6 +51,12 @@ var init_types = __esm({
|
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
// src/databaseResult.ts
|
|
54
|
+
function stripTypeArgs(colType) {
|
|
55
|
+
const open = colType.indexOf("(");
|
|
56
|
+
if (open < 0) return colType;
|
|
57
|
+
const close = colType.indexOf(")", open);
|
|
58
|
+
return close < 0 ? colType : colType.slice(0, open) + colType.slice(close + 1);
|
|
59
|
+
}
|
|
54
60
|
var DatabaseResult;
|
|
55
61
|
var init_databaseResult = __esm({
|
|
56
62
|
"src/databaseResult.ts"() {
|
|
@@ -233,7 +239,7 @@ var init_databaseResult = __esm({
|
|
|
233
239
|
const [size, decimals] = this._parseTypeSize(colType);
|
|
234
240
|
return {
|
|
235
241
|
name: col.name,
|
|
236
|
-
type: colType
|
|
242
|
+
type: stripTypeArgs(colType),
|
|
237
243
|
size,
|
|
238
244
|
decimals,
|
|
239
245
|
nullable: col.nullable ?? true,
|
|
@@ -790,12 +796,49 @@ var init_point = __esm({
|
|
|
790
796
|
});
|
|
791
797
|
|
|
792
798
|
// src/sqlTranslator.ts
|
|
793
|
-
|
|
799
|
+
function skipQuoted(sql, i, backslashEscapes) {
|
|
800
|
+
const c = sql[i];
|
|
801
|
+
const escapeString = c === "'" && (sql[i - 1] === "E" || sql[i - 1] === "e") && !isIdentChar(sql[i - 2]);
|
|
802
|
+
const backslash = c !== "`" && (escapeString || backslashEscapes);
|
|
803
|
+
const n = sql.length;
|
|
804
|
+
i++;
|
|
805
|
+
while (i < n) {
|
|
806
|
+
if (backslash && sql[i] === "\\") {
|
|
807
|
+
i += 2;
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
if (sql[i] === c) {
|
|
811
|
+
if (sql[i + 1] === c) {
|
|
812
|
+
i += 2;
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
return i + 1;
|
|
816
|
+
}
|
|
817
|
+
i++;
|
|
818
|
+
}
|
|
819
|
+
return i;
|
|
820
|
+
}
|
|
821
|
+
function skipLineComment(sql, i) {
|
|
822
|
+
while (i < sql.length && sql[i] !== "\n") i++;
|
|
823
|
+
return i;
|
|
824
|
+
}
|
|
825
|
+
function skipBlockComment(sql, i) {
|
|
826
|
+
const end = sql.indexOf("*/", i + 2);
|
|
827
|
+
return end === -1 ? sql.length : end + 2;
|
|
828
|
+
}
|
|
829
|
+
function skipDollarQuote(sql, i) {
|
|
830
|
+
const tag = sql.slice(i).match(/^\$([A-Za-z_][A-Za-z0-9_]*)?\$/);
|
|
831
|
+
if (!tag) return i;
|
|
832
|
+
const end = sql.indexOf(tag[0], i + tag[0].length);
|
|
833
|
+
return end === -1 ? sql.length : end + tag[0].length;
|
|
834
|
+
}
|
|
835
|
+
var isIdentChar, SQLTranslator, QueryCache;
|
|
794
836
|
var init_sqlTranslator = __esm({
|
|
795
837
|
"src/sqlTranslator.ts"() {
|
|
796
838
|
"use strict";
|
|
797
839
|
init_databaseUrl();
|
|
798
840
|
init_point();
|
|
841
|
+
isIdentChar = (ch) => !!ch && /[A-Za-z0-9_]/.test(ch);
|
|
799
842
|
SQLTranslator = class _SQLTranslator {
|
|
800
843
|
static SPATIAL_ENGINES = /* @__PURE__ */ new Set(["postgres", "postgresql"]);
|
|
801
844
|
static SPATIAL_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
@@ -1076,47 +1119,26 @@ var init_sqlTranslator = __esm({
|
|
|
1076
1119
|
static placeholderPositions(sql, options = {}) {
|
|
1077
1120
|
const positions = [];
|
|
1078
1121
|
const n = sql.length;
|
|
1079
|
-
const isIdent = (ch) => !!ch && /[A-Za-z0-9_]/.test(ch);
|
|
1080
1122
|
let i = 0;
|
|
1081
1123
|
while (i < n) {
|
|
1082
1124
|
const c = sql[i];
|
|
1083
1125
|
const next = sql[i + 1];
|
|
1084
1126
|
if (c === "'" || c === '"' || c === "`") {
|
|
1085
|
-
|
|
1086
|
-
const escapeString = c === "'" && (prev === "E" || prev === "e") && !isIdent(sql[i - 2]);
|
|
1087
|
-
const backslash = c !== "`" && (escapeString || options.backslashEscapes === true);
|
|
1088
|
-
i++;
|
|
1089
|
-
while (i < n) {
|
|
1090
|
-
if (backslash && sql[i] === "\\") {
|
|
1091
|
-
i += 2;
|
|
1092
|
-
continue;
|
|
1093
|
-
}
|
|
1094
|
-
if (sql[i] === c) {
|
|
1095
|
-
if (sql[i + 1] === c) {
|
|
1096
|
-
i += 2;
|
|
1097
|
-
continue;
|
|
1098
|
-
}
|
|
1099
|
-
i++;
|
|
1100
|
-
break;
|
|
1101
|
-
}
|
|
1102
|
-
i++;
|
|
1103
|
-
}
|
|
1127
|
+
i = skipQuoted(sql, i, options.backslashEscapes === true);
|
|
1104
1128
|
continue;
|
|
1105
1129
|
}
|
|
1106
1130
|
if (c === "-" && next === "-") {
|
|
1107
|
-
|
|
1131
|
+
i = skipLineComment(sql, i);
|
|
1108
1132
|
continue;
|
|
1109
1133
|
}
|
|
1110
1134
|
if (c === "/" && next === "*") {
|
|
1111
|
-
|
|
1112
|
-
i = end === -1 ? n : end + 2;
|
|
1135
|
+
i = skipBlockComment(sql, i);
|
|
1113
1136
|
continue;
|
|
1114
1137
|
}
|
|
1115
|
-
if (c === "$" && !
|
|
1116
|
-
const
|
|
1117
|
-
if (
|
|
1118
|
-
|
|
1119
|
-
i = end === -1 ? n : end + tag[0].length;
|
|
1138
|
+
if (c === "$" && !isIdentChar(sql[i - 1])) {
|
|
1139
|
+
const past = skipDollarQuote(sql, i);
|
|
1140
|
+
if (past !== i) {
|
|
1141
|
+
i = past;
|
|
1120
1142
|
continue;
|
|
1121
1143
|
}
|
|
1122
1144
|
}
|
|
@@ -1149,7 +1171,7 @@ var init_sqlTranslator = __esm({
|
|
|
1149
1171
|
* → { sql: "INSERT INTO t (x) VALUES (1)", columns: ["id", "name"] }
|
|
1150
1172
|
*/
|
|
1151
1173
|
static parseReturning(sql) {
|
|
1152
|
-
const m = sql.match(/\bRETURNING\s+(
|
|
1174
|
+
const m = sql.match(/\bRETURNING\s+(\S.*)$/i);
|
|
1153
1175
|
if (!m) return { sql, columns: [] };
|
|
1154
1176
|
const columns = m[1].split(",").map((c) => c.trim());
|
|
1155
1177
|
return {
|
|
@@ -1203,7 +1225,12 @@ var init_sqlTranslator = __esm({
|
|
|
1203
1225
|
// The `d` flag records group indices, so the head can be sliced at the exact
|
|
1204
1226
|
// start of the VALUES group rather than by hunting for a parenthesis (the
|
|
1205
1227
|
// column list has parentheses too).
|
|
1206
|
-
static INSERT_VALUES =
|
|
1228
|
+
static INSERT_VALUES = (
|
|
1229
|
+
// ReDoS-safe: the table ref matches `\S(?:[\s\S]*?\S)?` (bounded by non-space
|
|
1230
|
+
// at both ends) so the separator `\s+` before VALUES can't overlap it the way
|
|
1231
|
+
// the old `.+?\s+` did (polynomial on `INSERT INTO ` + a long space run).
|
|
1232
|
+
/^\s*INSERT\s+INTO\s+\S(?:[\s\S]*?\S)?\s+VALUES\s*\(([^()]*)\)\s*$/dis
|
|
1233
|
+
);
|
|
1207
1234
|
/**
|
|
1208
1235
|
* Engines whose lastInsertId reports the FIRST generated id of a multi-row
|
|
1209
1236
|
* INSERT rather than the last. Verified live, not assumed: a 3-row insert
|
|
@@ -1678,6 +1705,30 @@ var init_sqlDialect = __esm({
|
|
|
1678
1705
|
}
|
|
1679
1706
|
});
|
|
1680
1707
|
|
|
1708
|
+
// ../core/src/slashTrim.ts
|
|
1709
|
+
function rtrimSlashes(s) {
|
|
1710
|
+
let end = s.length;
|
|
1711
|
+
while (end > 0 && s.charCodeAt(end - 1) === SLASH) end--;
|
|
1712
|
+
return end === s.length ? s : s.slice(0, end);
|
|
1713
|
+
}
|
|
1714
|
+
function trimChar(s, code) {
|
|
1715
|
+
let start2 = 0;
|
|
1716
|
+
let end = s.length;
|
|
1717
|
+
while (start2 < end && s.charCodeAt(start2) === code) start2++;
|
|
1718
|
+
while (end > start2 && s.charCodeAt(end - 1) === code) end--;
|
|
1719
|
+
return start2 === 0 && end === s.length ? s : s.slice(start2, end);
|
|
1720
|
+
}
|
|
1721
|
+
function trimSlashes(s) {
|
|
1722
|
+
return trimChar(s, SLASH);
|
|
1723
|
+
}
|
|
1724
|
+
var SLASH;
|
|
1725
|
+
var init_slashTrim = __esm({
|
|
1726
|
+
"../core/src/slashTrim.ts"() {
|
|
1727
|
+
"use strict";
|
|
1728
|
+
SLASH = 47;
|
|
1729
|
+
}
|
|
1730
|
+
});
|
|
1731
|
+
|
|
1681
1732
|
// ../core/src/constants.ts
|
|
1682
1733
|
var HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, HTTP_NO_CONTENT, HTTP_MOVED, HTTP_REDIRECT, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, HTTP_METHOD_NOT_ALLOWED, HTTP_CONFLICT, HTTP_GONE, HTTP_UNPROCESSABLE, HTTP_TOO_MANY, HTTP_SERVER_ERROR, HTTP_BAD_GATEWAY, HTTP_UNAVAILABLE, APPLICATION_JSON, APPLICATION_XML, APPLICATION_FORM, APPLICATION_OCTET, TEXT_HTML, TEXT_PLAIN, TEXT_CSV, TEXT_XML;
|
|
1683
1734
|
var init_constants = __esm({
|
|
@@ -2766,7 +2817,7 @@ function resolveAlgorithm(algorithm) {
|
|
|
2766
2817
|
return chosen;
|
|
2767
2818
|
}
|
|
2768
2819
|
function base64urlEncode(data) {
|
|
2769
|
-
return data.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(
|
|
2820
|
+
return data.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/={1,2}$/, "");
|
|
2770
2821
|
}
|
|
2771
2822
|
function base64urlDecode(str) {
|
|
2772
2823
|
let s = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -3183,6 +3234,7 @@ function mayStore(req2, vary, cacheControl, setCookie) {
|
|
|
3183
3234
|
if (requestHeader(req2, "authorization") !== void 0) return sharedCacheAllowed(cacheControl);
|
|
3184
3235
|
if (requestHeader(req2, "cookie") !== void 0) return sharedCacheAllowed(cacheControl);
|
|
3185
3236
|
if (hasSetCookie(setCookie)) return sharedCacheAllowed(cacheControl);
|
|
3237
|
+
if (req2.session?.isFresh && !req2.session.isFresh()) return sharedCacheAllowed(cacheControl);
|
|
3186
3238
|
return true;
|
|
3187
3239
|
}
|
|
3188
3240
|
function varyMatches(entry, req2) {
|
|
@@ -4535,7 +4587,7 @@ function coerceParam(raw, type2) {
|
|
|
4535
4587
|
}
|
|
4536
4588
|
function joinGroupPath(prefix, path10) {
|
|
4537
4589
|
const full = `${prefix}/${path10.replace(/^\/+/, "")}`;
|
|
4538
|
-
const trimmed = `/${full
|
|
4590
|
+
const trimmed = `/${trimSlashes(full)}`;
|
|
4539
4591
|
return trimmed.replace(/\/+/g, "/");
|
|
4540
4592
|
}
|
|
4541
4593
|
async function resolveStringMiddleware(spec) {
|
|
@@ -4600,6 +4652,7 @@ var WsRouteRef, RouteRef, Router, RouteGroup, TINA4_DEFAULT_ROUTER, globalRouter
|
|
|
4600
4652
|
var init_router = __esm({
|
|
4601
4653
|
"../core/src/router.ts"() {
|
|
4602
4654
|
"use strict";
|
|
4655
|
+
init_slashTrim();
|
|
4603
4656
|
init_dotenv();
|
|
4604
4657
|
init_middleware();
|
|
4605
4658
|
WsRouteRef = class {
|
|
@@ -4809,7 +4862,7 @@ var init_router = __esm({
|
|
|
4809
4862
|
const direct = this.matchRoute(routes, path10);
|
|
4810
4863
|
if (direct) return direct;
|
|
4811
4864
|
if (isTrailingSlashRedirectEnabled() && path10.length > 1 && path10.endsWith("/")) {
|
|
4812
|
-
const stripped = path10
|
|
4865
|
+
const stripped = rtrimSlashes(path10);
|
|
4813
4866
|
if (stripped.length > 0) {
|
|
4814
4867
|
const retry = this.matchRoute(routes, stripped);
|
|
4815
4868
|
if (retry) return retry;
|
|
@@ -5488,9 +5541,11 @@ __export(engine_exports, {
|
|
|
5488
5541
|
escapeHtml: () => escapeHtml,
|
|
5489
5542
|
expressionFormCache: () => expressionFormCache,
|
|
5490
5543
|
filterChainCache: () => filterChainCache,
|
|
5544
|
+
frondRange: () => frondRange,
|
|
5491
5545
|
pathParseCache: () => pathParseCache,
|
|
5492
5546
|
setCspNonceProvider: () => setCspNonceProvider,
|
|
5493
|
-
setFormTokenSessionId: () => setFormTokenSessionId
|
|
5547
|
+
setFormTokenSessionId: () => setFormTokenSessionId,
|
|
5548
|
+
setFormTokenSessionProvider: () => setFormTokenSessionProvider
|
|
5494
5549
|
});
|
|
5495
5550
|
import { createHash as createHash3, createHmac as createHmac2, randomBytes as randomBytes3 } from "node:crypto";
|
|
5496
5551
|
import { readFileSync as readFileSync4, existsSync as existsSync5, statSync as statSync4, realpathSync } from "node:fs";
|
|
@@ -5575,6 +5630,14 @@ function renderDump(value) {
|
|
|
5575
5630
|
const escaped = dumped.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5576
5631
|
return new SafeString(`<pre>${escaped}</pre>`);
|
|
5577
5632
|
}
|
|
5633
|
+
function stripTags(input) {
|
|
5634
|
+
let s = input, prev;
|
|
5635
|
+
do {
|
|
5636
|
+
prev = s;
|
|
5637
|
+
s = s.replace(STRIP_TAGS_RE, "");
|
|
5638
|
+
} while (s !== prev);
|
|
5639
|
+
return s;
|
|
5640
|
+
}
|
|
5578
5641
|
function liveAttr(value) {
|
|
5579
5642
|
return String(value).replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
5580
5643
|
}
|
|
@@ -5592,35 +5655,58 @@ function sweepExpiredCache(cache) {
|
|
|
5592
5655
|
if (expiresAt2 <= now) cache.delete(key);
|
|
5593
5656
|
}
|
|
5594
5657
|
}
|
|
5595
|
-
function
|
|
5658
|
+
function extractRawBlocks(source) {
|
|
5596
5659
|
const rawBlocks = [];
|
|
5597
|
-
|
|
5660
|
+
let out = "";
|
|
5661
|
+
let pos = 0;
|
|
5662
|
+
RAW_OPEN_RE.lastIndex = 0;
|
|
5663
|
+
let om;
|
|
5664
|
+
while ((om = RAW_OPEN_RE.exec(source)) !== null) {
|
|
5665
|
+
const openStart = om.index;
|
|
5666
|
+
const openEnd = RAW_OPEN_RE.lastIndex;
|
|
5667
|
+
RAW_CLOSE_RE.lastIndex = openEnd;
|
|
5668
|
+
const cm = RAW_CLOSE_RE.exec(source);
|
|
5669
|
+
if (cm === null) break;
|
|
5598
5670
|
const idx = rawBlocks.length;
|
|
5599
|
-
rawBlocks.push(
|
|
5600
|
-
|
|
5601
|
-
|
|
5671
|
+
rawBlocks.push(source.slice(openEnd, cm.index));
|
|
5672
|
+
out += source.slice(pos, openStart) + `\0RAW_${idx}\0`;
|
|
5673
|
+
pos = RAW_CLOSE_RE.lastIndex;
|
|
5674
|
+
RAW_OPEN_RE.lastIndex = pos;
|
|
5675
|
+
}
|
|
5676
|
+
out += source.slice(pos);
|
|
5677
|
+
return { source: out, rawBlocks };
|
|
5678
|
+
}
|
|
5679
|
+
function tokenize(rawSource) {
|
|
5680
|
+
const { source, rawBlocks } = extractRawBlocks(rawSource);
|
|
5602
5681
|
const tokens = [];
|
|
5682
|
+
const n = source.length;
|
|
5603
5683
|
let pos = 0;
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
while (
|
|
5607
|
-
const
|
|
5608
|
-
if (
|
|
5609
|
-
|
|
5684
|
+
let scan = 0;
|
|
5685
|
+
const exhausted = { "%}": false, "}}": false, "#}": false };
|
|
5686
|
+
while (scan < n) {
|
|
5687
|
+
const brace = source.indexOf("{", scan);
|
|
5688
|
+
if (brace === -1) break;
|
|
5689
|
+
const kind = TAG_KINDS[source[brace + 1]];
|
|
5690
|
+
if (!kind) {
|
|
5691
|
+
scan = brace + 1;
|
|
5692
|
+
continue;
|
|
5610
5693
|
}
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
} else if (raw.startsWith("{{")) {
|
|
5615
|
-
tokens.push(["VAR", raw]);
|
|
5616
|
-
} else if (raw.startsWith("{%")) {
|
|
5617
|
-
tokens.push(["BLOCK", raw]);
|
|
5694
|
+
if (exhausted[kind.close]) {
|
|
5695
|
+
scan = brace + 1;
|
|
5696
|
+
continue;
|
|
5618
5697
|
}
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5698
|
+
const close = source.indexOf(kind.close, brace + 2);
|
|
5699
|
+
if (close === -1) {
|
|
5700
|
+
exhausted[kind.close] = true;
|
|
5701
|
+
scan = brace + 1;
|
|
5702
|
+
continue;
|
|
5703
|
+
}
|
|
5704
|
+
if (brace > pos) tokens.push(["TEXT", source.slice(pos, brace)]);
|
|
5705
|
+
tokens.push([kind.type, source.slice(brace, close + 2)]);
|
|
5706
|
+
pos = close + 2;
|
|
5707
|
+
scan = close + 2;
|
|
5623
5708
|
}
|
|
5709
|
+
if (pos < n) tokens.push(["TEXT", source.slice(pos)]);
|
|
5624
5710
|
if (rawBlocks.length > 0) {
|
|
5625
5711
|
for (let i = 0; i < tokens.length; i++) {
|
|
5626
5712
|
if (tokens[i][0] === "TEXT" && tokens[i][1].includes("\0RAW_")) {
|
|
@@ -5946,26 +6032,123 @@ function evalConcatOrComparison(expr, context) {
|
|
|
5946
6032
|
}
|
|
5947
6033
|
return EXPR_NOT_MATCHED;
|
|
5948
6034
|
}
|
|
6035
|
+
function topLevelPositions(expr) {
|
|
6036
|
+
const positions = [];
|
|
6037
|
+
let inQuote = null;
|
|
6038
|
+
let depth = 0;
|
|
6039
|
+
for (let pos = 0; pos < expr.length; pos++) {
|
|
6040
|
+
const ch = expr[pos];
|
|
6041
|
+
if (inQuote) {
|
|
6042
|
+
if (ch === inQuote) inQuote = null;
|
|
6043
|
+
} else if (ch === '"' || ch === "'") {
|
|
6044
|
+
inQuote = ch;
|
|
6045
|
+
} else if ("([{".includes(ch)) {
|
|
6046
|
+
depth++;
|
|
6047
|
+
} else if (")]}".includes(ch)) {
|
|
6048
|
+
depth--;
|
|
6049
|
+
} else if (depth === 0) {
|
|
6050
|
+
positions.push(pos);
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
return positions;
|
|
6054
|
+
}
|
|
6055
|
+
function followsOperand(expr, pos) {
|
|
6056
|
+
return /[\w)\]'"]$/.test(expr.slice(0, pos).trimEnd());
|
|
6057
|
+
}
|
|
6058
|
+
function topLevelArithmeticOperators(expr) {
|
|
6059
|
+
const operators = [];
|
|
6060
|
+
let skipUntil = -1;
|
|
6061
|
+
for (const pos of topLevelPositions(expr)) {
|
|
6062
|
+
if (pos < skipUntil || !"+-*/%".includes(expr[pos])) continue;
|
|
6063
|
+
const pair = expr.slice(pos, pos + 2);
|
|
6064
|
+
const op = pair === "**" || pair === "//" ? pair : expr[pos];
|
|
6065
|
+
skipUntil = pos + op.length;
|
|
6066
|
+
if (followsOperand(expr, pos)) operators.push([pos, op]);
|
|
6067
|
+
}
|
|
6068
|
+
return operators;
|
|
6069
|
+
}
|
|
6070
|
+
function splitArithmetic(expr) {
|
|
6071
|
+
const operators = topLevelArithmeticOperators(expr);
|
|
6072
|
+
for (const level of ARITHMETIC_LEVELS) {
|
|
6073
|
+
const matches = operators.filter(([, op]) => level.includes(op));
|
|
6074
|
+
if (matches.length > 0) return level[0] === "**" ? matches[0] : matches[matches.length - 1];
|
|
6075
|
+
}
|
|
6076
|
+
return null;
|
|
6077
|
+
}
|
|
5949
6078
|
function evalArithmeticExpression(expr, context) {
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
6079
|
+
const split = splitArithmetic(expr);
|
|
6080
|
+
if (!split) return EXPR_NOT_MATCHED;
|
|
6081
|
+
const [pos, op] = split;
|
|
6082
|
+
const lVal = evalExpr(expr.slice(0, pos).trim(), context);
|
|
6083
|
+
const rVal = evalExpr(expr.slice(pos + op.length).trim(), context);
|
|
6084
|
+
let lNum = lVal != null ? Number(lVal) : 0;
|
|
6085
|
+
let rNum = rVal != null ? Number(rVal) : 0;
|
|
6086
|
+
if (isNaN(lNum)) lNum = 0;
|
|
6087
|
+
if (isNaN(rNum)) rNum = 0;
|
|
6088
|
+
return ARITHMETIC_OPERATIONS[op](lNum, rNum);
|
|
6089
|
+
}
|
|
6090
|
+
function frondRange(start2, end, step = 1) {
|
|
6091
|
+
const numeric = (value) => typeof value === "number" || typeof value === "string" && NUMERIC_RE.test(value);
|
|
6092
|
+
if (!numeric(start2) || !numeric(end) || !numeric(step)) return [];
|
|
6093
|
+
const first = Math.trunc(Number(start2));
|
|
6094
|
+
const last = Math.trunc(Number(end));
|
|
6095
|
+
const stride = Math.abs(Math.trunc(Number(step)));
|
|
6096
|
+
if (stride === 0) return [];
|
|
6097
|
+
const values = [];
|
|
6098
|
+
if (first <= last) for (let value = first; value <= last; value += stride) values.push(value);
|
|
6099
|
+
else for (let value = first; value >= last; value -= stride) values.push(value);
|
|
6100
|
+
return values;
|
|
6101
|
+
}
|
|
6102
|
+
function findRangeOperator(expr) {
|
|
6103
|
+
let pos = findOutsideQuotes(expr, "..");
|
|
6104
|
+
while (pos >= 0) {
|
|
6105
|
+
const partOfEllipsis = expr.startsWith("...", pos) || pos > 0 && expr[pos - 1] === ".";
|
|
6106
|
+
if (pos > 0 && !partOfEllipsis) return findOutsideQuotes(expr.slice(pos + 2), "|") >= 0 ? -1 : pos;
|
|
6107
|
+
const next = findOutsideQuotes(expr.slice(pos + 2), "..");
|
|
6108
|
+
pos = next >= 0 ? pos + 2 + next : -1;
|
|
6109
|
+
}
|
|
6110
|
+
return -1;
|
|
6111
|
+
}
|
|
6112
|
+
function evalRangeExpression(expr, context) {
|
|
6113
|
+
const pos = findRangeOperator(expr);
|
|
6114
|
+
if (pos < 0) return EXPR_NOT_MATCHED;
|
|
6115
|
+
return frondRange(evalExpr(expr.slice(0, pos).trim(), context), evalExpr(expr.slice(pos + 2).trim(), context));
|
|
6116
|
+
}
|
|
6117
|
+
function closingBracketIndex(expr) {
|
|
6118
|
+
let depth = 0;
|
|
6119
|
+
let inQuote = null;
|
|
6120
|
+
for (let i = 0; i < expr.length; i++) {
|
|
6121
|
+
const ch = expr[i];
|
|
6122
|
+
if (inQuote) {
|
|
6123
|
+
if (ch === inQuote) inQuote = null;
|
|
6124
|
+
} else if (ch === '"' || ch === "'") {
|
|
6125
|
+
inQuote = ch;
|
|
6126
|
+
} else if ("([{".includes(ch)) {
|
|
6127
|
+
depth++;
|
|
6128
|
+
} else if (")]}".includes(ch) && --depth === 0) {
|
|
6129
|
+
return i;
|
|
5966
6130
|
}
|
|
5967
6131
|
}
|
|
5968
|
-
return
|
|
6132
|
+
return -1;
|
|
6133
|
+
}
|
|
6134
|
+
function mapLiteralKey(rawKey, context) {
|
|
6135
|
+
const quoted = rawKey.length >= 2 && (rawKey[0] === '"' || rawKey[0] === "'") && rawKey.endsWith(rawKey[0]);
|
|
6136
|
+
if (quoted) return rawKey.slice(1, -1);
|
|
6137
|
+
return BARE_KEY_RE.test(rawKey) ? rawKey : String(evalExpr(rawKey, context));
|
|
6138
|
+
}
|
|
6139
|
+
function evalMapLiteral(inner, context) {
|
|
6140
|
+
const map = {};
|
|
6141
|
+
for (const pair of inner === "" ? [] : splitArgs(inner)) {
|
|
6142
|
+
const colon = findOutsideQuotes(pair, ":");
|
|
6143
|
+
if (colon > 0) map[mapLiteralKey(pair.slice(0, colon).trim(), context)] = evalExpr(pair.slice(colon + 1).trim(), context);
|
|
6144
|
+
}
|
|
6145
|
+
return map;
|
|
6146
|
+
}
|
|
6147
|
+
function evalCollectionLiteral(expr, context) {
|
|
6148
|
+
if (expr[0] !== "[" && expr[0] !== "{" || closingBracketIndex(expr) !== expr.length - 1) return EXPR_NOT_MATCHED;
|
|
6149
|
+
const inner = expr.slice(1, -1).trim();
|
|
6150
|
+
if (expr[0] === "{") return evalMapLiteral(inner, context);
|
|
6151
|
+
return inner === "" ? [] : splitArgs(inner).map((item) => evalExpr(item.trim(), context));
|
|
5969
6152
|
}
|
|
5970
6153
|
function evalFilterExpression(expr, context) {
|
|
5971
6154
|
if (findOutsideQuotes(expr, "|") >= 0) {
|
|
@@ -6387,12 +6570,12 @@ function splitArgs(raw) {
|
|
|
6387
6570
|
current += ch;
|
|
6388
6571
|
continue;
|
|
6389
6572
|
}
|
|
6390
|
-
if (ch === "(" || ch === "[") {
|
|
6573
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
6391
6574
|
depth++;
|
|
6392
6575
|
current += ch;
|
|
6393
6576
|
continue;
|
|
6394
6577
|
}
|
|
6395
|
-
if (ch === ")" || ch === "]") {
|
|
6578
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
6396
6579
|
depth--;
|
|
6397
6580
|
current += ch;
|
|
6398
6581
|
continue;
|
|
@@ -6557,11 +6740,14 @@ function formatMatch(match, flags, width, prec, type2, arg) {
|
|
|
6557
6740
|
return padFormattedValue(formatValue(type2, precision, arg), flags, width);
|
|
6558
6741
|
}
|
|
6559
6742
|
function _b64url(data) {
|
|
6560
|
-
return data.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(
|
|
6743
|
+
return data.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/={1,2}$/, "");
|
|
6561
6744
|
}
|
|
6562
6745
|
function setFormTokenSessionId(sessionId) {
|
|
6563
6746
|
_formTokenSessionId = sessionId || "";
|
|
6564
6747
|
}
|
|
6748
|
+
function setFormTokenSessionProvider(fn) {
|
|
6749
|
+
_formTokenSessionProvider = fn;
|
|
6750
|
+
}
|
|
6565
6751
|
function setCspNonceProvider(fn) {
|
|
6566
6752
|
_cspNonceProvider = fn;
|
|
6567
6753
|
}
|
|
@@ -6586,8 +6772,9 @@ function _buildFormTokenJwt(descriptor = "") {
|
|
|
6586
6772
|
payload.context = descriptor;
|
|
6587
6773
|
}
|
|
6588
6774
|
}
|
|
6589
|
-
|
|
6590
|
-
|
|
6775
|
+
const sessionId = _formTokenSessionProvider() || _formTokenSessionId;
|
|
6776
|
+
if (sessionId) {
|
|
6777
|
+
payload.session_id = sessionId;
|
|
6591
6778
|
}
|
|
6592
6779
|
const h2 = _b64url(Buffer.from(JSON.stringify(header)));
|
|
6593
6780
|
const p = _b64url(Buffer.from(JSON.stringify(payload)));
|
|
@@ -6603,7 +6790,7 @@ function _generateFormToken(descriptor = "") {
|
|
|
6603
6790
|
function _generateFormTokenValue(descriptor = "") {
|
|
6604
6791
|
return new SafeString(_buildFormTokenJwt(descriptor));
|
|
6605
6792
|
}
|
|
6606
|
-
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, FAST_FILTERS, FORMAT_RE,
|
|
6793
|
+
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, FAST_FILTERS, FORMAT_RE, THOUSANDS_RE, LIVE_RE, LIVE_WS_RE, LIVE_SRC_RE, EXTENDS_RE, EXTENDS_RE_GLOBAL, filterChainCache, pathParseCache, TEMPLATE_CACHE_MAX, MEMO_CACHE_MAX, RAW_OPEN_RE, RAW_CLOSE_RE, TAG_KINDS, EXPR_NOT_MATCHED, ARITHMETIC_OPERATIONS, ARITHMETIC_LEVELS, BARE_KEY_RE, EXPR_EVALUATORS, expressionFormCache, VarRef, JS_SAFE_RE, MEDIA_TYPE_RE, SANDBOX_EXPR_KEYWORDS, SANDBOX_IDENT_RE, SANDBOX_STRING_RE, UNSAFE_KEYS, BUILTIN_FILTERS, _formTokenSessionId, _formTokenSessionProvider, _cspNonceProvider, Frond;
|
|
6607
6794
|
var init_engine = __esm({
|
|
6608
6795
|
"../frond/src/engine.ts"() {
|
|
6609
6796
|
"use strict";
|
|
@@ -6705,7 +6892,7 @@ var init_engine = __esm({
|
|
|
6705
6892
|
int: (value) => value ? parseInt(String(value), 10) || 0 : 0,
|
|
6706
6893
|
float: (value) => value ? parseFloat(String(value)) || 0 : 0,
|
|
6707
6894
|
abs: (value) => typeof value === "number" ? Math.abs(value) : value,
|
|
6708
|
-
striptags: (value) => String(value)
|
|
6895
|
+
striptags: (value) => stripTags(String(value)),
|
|
6709
6896
|
first: (value) => Array.isArray(value) ? value[0] ?? null : null,
|
|
6710
6897
|
last: (value) => Array.isArray(value) ? value[value.length - 1] ?? null : null,
|
|
6711
6898
|
keys: (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value) : [],
|
|
@@ -6719,20 +6906,23 @@ var init_engine = __esm({
|
|
|
6719
6906
|
filter: (value) => Array.isArray(value) ? value.filter(Boolean) : value
|
|
6720
6907
|
};
|
|
6721
6908
|
FORMAT_RE = /%%|%([-+ 0]*)(\d+)?(?:\.(\d+))?([sdifFeEgGxXob])/g;
|
|
6722
|
-
LEADING_WS_RE = /^\s+/;
|
|
6723
|
-
TRAILING_WS_RE = /\s+$/;
|
|
6724
6909
|
THOUSANDS_RE = /\B(?=(\d{3})+(?!\d))/g;
|
|
6725
6910
|
LIVE_RE = /^live\s+["']([^"']+)["']([\s\S]*)$/;
|
|
6726
6911
|
LIVE_WS_RE = /ws\s+["']([^"']+)["']/;
|
|
6727
6912
|
LIVE_SRC_RE = /src\s+["']([^"']+)["']/;
|
|
6728
|
-
EXTENDS_RE = /\{%[-\s]*extends\s+["'](
|
|
6729
|
-
EXTENDS_RE_GLOBAL = /\{%[-\s]*extends\s+["'](
|
|
6913
|
+
EXTENDS_RE = /\{%[-\s]*extends\s+["']([^"']+)["']\s*[-]?%\}/;
|
|
6914
|
+
EXTENDS_RE_GLOBAL = /\{%[-\s]*extends\s+["']([^"']+)["']\s*[-]?%\}/g;
|
|
6730
6915
|
filterChainCache = /* @__PURE__ */ new Map();
|
|
6731
6916
|
pathParseCache = /* @__PURE__ */ new Map();
|
|
6732
6917
|
TEMPLATE_CACHE_MAX = 256;
|
|
6733
6918
|
MEMO_CACHE_MAX = 1024;
|
|
6734
|
-
|
|
6735
|
-
|
|
6919
|
+
RAW_OPEN_RE = /\{%-?\s*raw\s*-?%\}/g;
|
|
6920
|
+
RAW_CLOSE_RE = /\{%-?\s*endraw\s*-?%\}/g;
|
|
6921
|
+
TAG_KINDS = {
|
|
6922
|
+
"%": { close: "%}", type: "BLOCK" },
|
|
6923
|
+
"{": { close: "}}", type: "VAR" },
|
|
6924
|
+
"#": { close: "#}", type: "COMMENT" }
|
|
6925
|
+
};
|
|
6736
6926
|
EXPR_NOT_MATCHED = /* @__PURE__ */ Symbol("frond-expression-not-matched");
|
|
6737
6927
|
ARITHMETIC_OPERATIONS = {
|
|
6738
6928
|
"+": (left, right) => left + right,
|
|
@@ -6743,10 +6933,14 @@ var init_engine = __esm({
|
|
|
6743
6933
|
"%": (left, right) => right !== 0 ? left % right : 0,
|
|
6744
6934
|
"**": (left, right) => left ** right
|
|
6745
6935
|
};
|
|
6936
|
+
ARITHMETIC_LEVELS = [["+", "-"], ["*", "//", "/", "%"], ["**"]];
|
|
6937
|
+
BARE_KEY_RE = /^[A-Za-z_]\w*$/;
|
|
6746
6938
|
EXPR_EVALUATORS = [
|
|
6939
|
+
evalCollectionLiteral,
|
|
6747
6940
|
evalPrimary,
|
|
6748
6941
|
evalConditional,
|
|
6749
6942
|
evalConcatOrComparison,
|
|
6943
|
+
evalRangeExpression,
|
|
6750
6944
|
evalArithmeticExpression,
|
|
6751
6945
|
evalFilterExpression,
|
|
6752
6946
|
evalFunctionExpression
|
|
@@ -6802,8 +6996,8 @@ var init_engine = __esm({
|
|
|
6802
6996
|
},
|
|
6803
6997
|
title: (v) => String(v).replace(TITLE_WORD_RE, (c) => c.toUpperCase()),
|
|
6804
6998
|
trim: (v) => String(v).trim(),
|
|
6805
|
-
ltrim: (v) => String(v).
|
|
6806
|
-
rtrim: (v) => String(v).
|
|
6999
|
+
ltrim: (v) => String(v).trimStart(),
|
|
7000
|
+
rtrim: (v) => String(v).trimEnd(),
|
|
6807
7001
|
length: (v) => {
|
|
6808
7002
|
if (Array.isArray(v)) return v.length;
|
|
6809
7003
|
if (typeof v === "string") return v.length;
|
|
@@ -6844,7 +7038,7 @@ var init_engine = __esm({
|
|
|
6844
7038
|
safe: (v) => v,
|
|
6845
7039
|
escape: (v, ...a) => escapeStrategy(v, a[0] ?? "html"),
|
|
6846
7040
|
e: (v, ...a) => escapeStrategy(v, a[0] ?? "html"),
|
|
6847
|
-
striptags: (v) => String(v)
|
|
7041
|
+
striptags: (v) => stripTags(String(v)),
|
|
6848
7042
|
nl2br: (v) => new SafeString(htmlEscape(String(v)).replace(/\n/g, "<br />\n")),
|
|
6849
7043
|
abs: (v) => typeof v === "number" ? Math.abs(v) : v,
|
|
6850
7044
|
round: (v, decimals) => {
|
|
@@ -6957,6 +7151,7 @@ var init_engine = __esm({
|
|
|
6957
7151
|
js_escape: (v) => new SafeString(jsEscape(v))
|
|
6958
7152
|
};
|
|
6959
7153
|
_formTokenSessionId = "";
|
|
7154
|
+
_formTokenSessionProvider = () => "";
|
|
6960
7155
|
_cspNonceProvider = () => "";
|
|
6961
7156
|
Frond = class _Frond {
|
|
6962
7157
|
// ── Class-level registries ──────────────────────────────────
|
|
@@ -7110,6 +7305,7 @@ var init_engine = __esm({
|
|
|
7110
7305
|
this.globals.form_token = (descriptor) => _generateFormToken(descriptor || "");
|
|
7111
7306
|
this.globals.formTokenValue = (descriptor) => _generateFormTokenValue(descriptor || "");
|
|
7112
7307
|
this.globals.form_token_value = (descriptor) => _generateFormTokenValue(descriptor || "");
|
|
7308
|
+
this.globals.range = frondRange;
|
|
7113
7309
|
this.globals.dump = (value) => renderDump(value);
|
|
7114
7310
|
this.globals.csp_nonce = () => _cspNonceProvider();
|
|
7115
7311
|
for (const [k, v] of _Frond.classFilters) this.filters[k] = v;
|
|
@@ -7446,20 +7642,20 @@ var init_engine = __esm({
|
|
|
7446
7642
|
}
|
|
7447
7643
|
renderVarToken(tokens, index, context, output) {
|
|
7448
7644
|
const [content, stripB, stripA] = stripTag(tokens[index][1]);
|
|
7449
|
-
if (stripB && output.length > 0) output[output.length - 1] = output[output.length - 1].
|
|
7645
|
+
if (stripB && output.length > 0) output[output.length - 1] = output[output.length - 1].trimEnd();
|
|
7450
7646
|
const result = this.evalVar(content, context);
|
|
7451
7647
|
output.push(result !== null && result !== void 0 ? String(result) : "");
|
|
7452
7648
|
if (stripA && index + 1 < tokens.length && tokens[index + 1][0] === "TEXT") {
|
|
7453
|
-
tokens[index + 1] = ["TEXT", tokens[index + 1][1].
|
|
7649
|
+
tokens[index + 1] = ["TEXT", tokens[index + 1][1].trimStart()];
|
|
7454
7650
|
}
|
|
7455
7651
|
return index + 1;
|
|
7456
7652
|
}
|
|
7457
7653
|
renderBlockToken(tokens, index, context, output) {
|
|
7458
7654
|
const [content, stripB, stripA] = stripTag(tokens[index][1]);
|
|
7459
|
-
if (stripB && output.length > 0) output[output.length - 1] = output[output.length - 1].
|
|
7655
|
+
if (stripB && output.length > 0) output[output.length - 1] = output[output.length - 1].trimEnd();
|
|
7460
7656
|
const tag = content.split(/\s+/)[0] || "";
|
|
7461
7657
|
if (stripA && index + 1 < tokens.length && tokens[index + 1][0] === "TEXT") {
|
|
7462
|
-
tokens[index + 1] = ["TEXT", tokens[index + 1][1].
|
|
7658
|
+
tokens[index + 1] = ["TEXT", tokens[index + 1][1].trimStart()];
|
|
7463
7659
|
}
|
|
7464
7660
|
let next;
|
|
7465
7661
|
if (tag === "if" && this.tagPermitted(tag)) {
|
|
@@ -7476,7 +7672,7 @@ var init_engine = __esm({
|
|
|
7476
7672
|
next = block.next;
|
|
7477
7673
|
}
|
|
7478
7674
|
if (stripA && next < tokens.length && tokens[next][0] === "TEXT") {
|
|
7479
|
-
tokens[next] = ["TEXT", tokens[next][1].
|
|
7675
|
+
tokens[next] = ["TEXT", tokens[next][1].trimStart()];
|
|
7480
7676
|
}
|
|
7481
7677
|
return next;
|
|
7482
7678
|
}
|
|
@@ -7735,7 +7931,7 @@ var init_engine = __esm({
|
|
|
7735
7931
|
pushIfBranch(branches, condition, branchTokens, stripBefore) {
|
|
7736
7932
|
if (stripBefore && branchTokens.length > 0 && branchTokens[branchTokens.length - 1][0] === "TEXT") {
|
|
7737
7933
|
const last = branchTokens[branchTokens.length - 1];
|
|
7738
|
-
branchTokens[branchTokens.length - 1] = ["TEXT", last[1].
|
|
7934
|
+
branchTokens[branchTokens.length - 1] = ["TEXT", last[1].trimEnd()];
|
|
7739
7935
|
}
|
|
7740
7936
|
branches.push([condition, branchTokens]);
|
|
7741
7937
|
}
|
|
@@ -7763,7 +7959,7 @@ var init_engine = __esm({
|
|
|
7763
7959
|
} else if (tag === "endif") {
|
|
7764
7960
|
this.pushIfBranch(branches, currentCond, currentTokens, tagStripB);
|
|
7765
7961
|
if (tagStripA && i + 1 < tokens.length && tokens[i + 1][0] === "TEXT") {
|
|
7766
|
-
tokens[i + 1] = ["TEXT", tokens[i + 1][1].
|
|
7962
|
+
tokens[i + 1] = ["TEXT", tokens[i + 1][1].trimStart()];
|
|
7767
7963
|
}
|
|
7768
7964
|
return { branches, next: i + 1 };
|
|
7769
7965
|
} else if ((tag === "elseif" || tag === "elif") && depth === 0) {
|
|
@@ -9412,7 +9608,8 @@ var init_middleware = __esm({
|
|
|
9412
9608
|
"Content-Security-Policy": resolveCspHeader(currentCspNonce()),
|
|
9413
9609
|
"Referrer-Policy": process.env.TINA4_REFERRER_POLICY ?? "strict-origin-when-cross-origin",
|
|
9414
9610
|
"X-XSS-Protection": "0",
|
|
9415
|
-
"Permissions-Policy": process.env.TINA4_PERMISSIONS_POLICY ?? "camera=(), microphone=(), geolocation=()"
|
|
9611
|
+
"Permissions-Policy": process.env.TINA4_PERMISSIONS_POLICY ?? "camera=(), microphone=(), geolocation=()",
|
|
9612
|
+
"Cross-Origin-Opener-Policy": process.env.TINA4_COOP ?? "same-origin-allow-popups"
|
|
9416
9613
|
};
|
|
9417
9614
|
}
|
|
9418
9615
|
static beforeSecurity(req2, res) {
|
|
@@ -9429,7 +9626,7 @@ var init_middleware = __esm({
|
|
|
9429
9626
|
if (process.env.TINA4_CSP === void 0) {
|
|
9430
9627
|
_SecurityHeadersMiddleware.warnCspDefaultOnce();
|
|
9431
9628
|
}
|
|
9432
|
-
for (const name of ["Content-Security-Policy", "Referrer-Policy", "X-XSS-Protection", "Permissions-Policy"]) {
|
|
9629
|
+
for (const name of ["Content-Security-Policy", "Referrer-Policy", "X-XSS-Protection", "Permissions-Policy", "Cross-Origin-Opener-Policy"]) {
|
|
9433
9630
|
res.header(name, canonical[name]);
|
|
9434
9631
|
}
|
|
9435
9632
|
return [req2, res];
|
|
@@ -9513,7 +9710,7 @@ var init_middleware = __esm({
|
|
|
9513
9710
|
});
|
|
9514
9711
|
|
|
9515
9712
|
// ../core/src/request.ts
|
|
9516
|
-
import { mkdirSync as mkdirSync3, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
9713
|
+
import { closeSync as closeSync3, constants as constants2, fstatSync as fstatSync2, ftruncateSync, lstatSync, mkdirSync as mkdirSync3, openSync as openSync3, realpathSync as realpathSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
9517
9714
|
import { dirname as dirname3, join as join6 } from "node:path";
|
|
9518
9715
|
function makeCaseInsensitiveHeaders(raw) {
|
|
9519
9716
|
return new Proxy(raw, {
|
|
@@ -9549,7 +9746,8 @@ function createRequest(req2) {
|
|
|
9549
9746
|
try {
|
|
9550
9747
|
const url = new URL(req2.url ?? "/", `${proto}://${host}`);
|
|
9551
9748
|
for (const [key, value] of url.searchParams) {
|
|
9552
|
-
|
|
9749
|
+
if (isUnsafeFieldName(key)) continue;
|
|
9750
|
+
Reflect.set(query, key, value);
|
|
9553
9751
|
}
|
|
9554
9752
|
path10 = url.pathname;
|
|
9555
9753
|
queryString = url.search.replace(/^\?/, "");
|
|
@@ -9561,7 +9759,8 @@ function createRequest(req2) {
|
|
|
9561
9759
|
queryString = questionIdx >= 0 ? rawTarget.slice(questionIdx + 1) : "";
|
|
9562
9760
|
try {
|
|
9563
9761
|
for (const [key, value] of new URLSearchParams(queryString)) {
|
|
9564
|
-
|
|
9762
|
+
if (isUnsafeFieldName(key)) continue;
|
|
9763
|
+
Reflect.set(query, key, value);
|
|
9565
9764
|
}
|
|
9566
9765
|
} catch {
|
|
9567
9766
|
}
|
|
@@ -9580,7 +9779,8 @@ function createRequest(req2) {
|
|
|
9580
9779
|
if (cookieHeader) {
|
|
9581
9780
|
for (const pair of cookieHeader.split(";")) {
|
|
9582
9781
|
const [k, ...v] = pair.trim().split("=");
|
|
9583
|
-
|
|
9782
|
+
const name = k ? k.trim() : "";
|
|
9783
|
+
if (name && !isUnsafeFieldName(name)) Reflect.set(cookies, name, v.join("=").trim());
|
|
9584
9784
|
}
|
|
9585
9785
|
}
|
|
9586
9786
|
w.cookies = cookies;
|
|
@@ -9671,13 +9871,7 @@ async function parseBody(req2) {
|
|
|
9671
9871
|
req2.body = str;
|
|
9672
9872
|
}
|
|
9673
9873
|
} else if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
9674
|
-
|
|
9675
|
-
const params = new URLSearchParams(str);
|
|
9676
|
-
const obj = {};
|
|
9677
|
-
for (const [key, value] of params) {
|
|
9678
|
-
obj[key] = value;
|
|
9679
|
-
}
|
|
9680
|
-
req2.body = obj;
|
|
9874
|
+
req2.body = nestFormFields([...new URLSearchParams(raw.toString("utf-8"))]);
|
|
9681
9875
|
} else {
|
|
9682
9876
|
req2.body = raw.toString("utf-8");
|
|
9683
9877
|
}
|
|
@@ -9687,7 +9881,7 @@ function extractBoundary(contentType) {
|
|
|
9687
9881
|
return match ? match[1] ?? match[2] : null;
|
|
9688
9882
|
}
|
|
9689
9883
|
function parseMultipart(body, boundary) {
|
|
9690
|
-
const
|
|
9884
|
+
const pairs = [];
|
|
9691
9885
|
const files = {};
|
|
9692
9886
|
const delimiter2 = Buffer.from(`--${boundary}`);
|
|
9693
9887
|
const closeDelimiter = Buffer.from(`--${boundary}--`);
|
|
@@ -9695,7 +9889,7 @@ function parseMultipart(body, boundary) {
|
|
|
9695
9889
|
const doubleCrlf = Buffer.from("\r\n\r\n");
|
|
9696
9890
|
let offset = 0;
|
|
9697
9891
|
const firstIdx = bufferIndexOf(body, delimiter2, offset);
|
|
9698
|
-
if (firstIdx === -1) return { fields, files };
|
|
9892
|
+
if (firstIdx === -1) return { fields: {}, files };
|
|
9699
9893
|
offset = firstIdx + delimiter2.length;
|
|
9700
9894
|
if (body[offset] === 13 && body[offset + 1] === 10) {
|
|
9701
9895
|
offset += 2;
|
|
@@ -9719,14 +9913,15 @@ function parseMultipart(body, boundary) {
|
|
|
9719
9913
|
content: Buffer.from(content),
|
|
9720
9914
|
size: content.length
|
|
9721
9915
|
};
|
|
9722
|
-
if (
|
|
9916
|
+
if (isUnsafeFieldName(disposition.name)) {
|
|
9917
|
+
} else if (files[disposition.name]) {
|
|
9723
9918
|
const existing = files[disposition.name];
|
|
9724
|
-
files
|
|
9919
|
+
Reflect.set(files, disposition.name, Array.isArray(existing) ? [...existing, file] : [existing, file]);
|
|
9725
9920
|
} else {
|
|
9726
|
-
files
|
|
9921
|
+
Reflect.set(files, disposition.name, file);
|
|
9727
9922
|
}
|
|
9728
9923
|
} else if (disposition.name) {
|
|
9729
|
-
|
|
9924
|
+
pairs.push([disposition.name, content.toString("utf-8")]);
|
|
9730
9925
|
}
|
|
9731
9926
|
offset = nextDelimIdx + delimiter2.length;
|
|
9732
9927
|
if (body[offset] === 45 && body[offset + 1] === 45) {
|
|
@@ -9736,7 +9931,7 @@ function parseMultipart(body, boundary) {
|
|
|
9736
9931
|
offset += 2;
|
|
9737
9932
|
}
|
|
9738
9933
|
}
|
|
9739
|
-
return { fields, files };
|
|
9934
|
+
return { fields: nestFormFields(pairs), files };
|
|
9740
9935
|
}
|
|
9741
9936
|
function saveUpload(file, targetDir, filename) {
|
|
9742
9937
|
const raw = String(filename ?? file.filename ?? "");
|
|
@@ -9752,7 +9947,21 @@ function saveUpload(file, targetDir, filename) {
|
|
|
9752
9947
|
if (realParent !== realDir) {
|
|
9753
9948
|
throw new Error(`refusing to write outside ${targetDir}: ${JSON.stringify(raw)}`);
|
|
9754
9949
|
}
|
|
9755
|
-
|
|
9950
|
+
if (lstatSync(dest, { throwIfNoEntry: false })?.isSymbolicLink()) unlinkSync3(dest);
|
|
9951
|
+
const flags = constants2.O_WRONLY | constants2.O_CREAT | (constants2.O_NOFOLLOW ?? 0);
|
|
9952
|
+
let fd = openSync3(dest, flags, 384);
|
|
9953
|
+
try {
|
|
9954
|
+
if (fstatSync2(fd).nlink > 1) {
|
|
9955
|
+
closeSync3(fd);
|
|
9956
|
+
fd = -1;
|
|
9957
|
+
unlinkSync3(dest);
|
|
9958
|
+
fd = openSync3(dest, flags | constants2.O_EXCL, 384);
|
|
9959
|
+
}
|
|
9960
|
+
ftruncateSync(fd, 0);
|
|
9961
|
+
writeFileSync2(fd, file.content);
|
|
9962
|
+
} finally {
|
|
9963
|
+
if (fd >= 0) closeSync3(fd);
|
|
9964
|
+
}
|
|
9756
9965
|
return dest;
|
|
9757
9966
|
}
|
|
9758
9967
|
function bufferIndexOf(haystack, needle, offset) {
|
|
@@ -9777,10 +9986,98 @@ function parseDisposition(headers) {
|
|
|
9777
9986
|
};
|
|
9778
9987
|
}
|
|
9779
9988
|
function parsePartContentType(headers) {
|
|
9780
|
-
const match = headers.match(/Content-Type
|
|
9781
|
-
|
|
9989
|
+
const match = headers.match(/Content-Type:([^\r\n]*)/i);
|
|
9990
|
+
const value = match?.[1]?.trim();
|
|
9991
|
+
return value ? value : null;
|
|
9992
|
+
}
|
|
9993
|
+
function nestFormFields(pairs) {
|
|
9994
|
+
const kept = pairs.filter(([name]) => !/(^|[[\]])__proto__([[\]]|$)/.test(name));
|
|
9995
|
+
if (kept.length > FORM_FIELD_LIMIT) return Object.fromEntries(kept);
|
|
9996
|
+
const nested = /* @__PURE__ */ new WeakSet();
|
|
9997
|
+
const node = () => {
|
|
9998
|
+
const created = /* @__PURE__ */ Object.create(null);
|
|
9999
|
+
nested.add(created);
|
|
10000
|
+
return created;
|
|
10001
|
+
};
|
|
10002
|
+
const isNode = (value) => typeof value === "object" && value !== null && nested.has(value);
|
|
10003
|
+
const own = (params, key) => Object.prototype.hasOwnProperty.call(params, key) ? params[key] : void 0;
|
|
10004
|
+
const list = (params, key) => {
|
|
10005
|
+
let current = own(params, key);
|
|
10006
|
+
if (current === void 0) {
|
|
10007
|
+
current = [];
|
|
10008
|
+
Reflect.set(params, key, current);
|
|
10009
|
+
}
|
|
10010
|
+
if (!Array.isArray(current)) throw new RangeError(`expected an array for '${key}'`);
|
|
10011
|
+
return current;
|
|
10012
|
+
};
|
|
10013
|
+
const object = (params, key) => {
|
|
10014
|
+
let current = own(params, key);
|
|
10015
|
+
if (current === void 0) {
|
|
10016
|
+
current = node();
|
|
10017
|
+
Reflect.set(params, key, current);
|
|
10018
|
+
}
|
|
10019
|
+
if (!isNode(current)) throw new RangeError(`expected an object for '${key}'`);
|
|
10020
|
+
return current;
|
|
10021
|
+
};
|
|
10022
|
+
const hasKey = (params, key) => {
|
|
10023
|
+
if (key.includes("[]")) return false;
|
|
10024
|
+
let level = params;
|
|
10025
|
+
for (const part of key.split(/[[\]]+/).filter(Boolean)) {
|
|
10026
|
+
if (!isNode(level) || own(level, part) === void 0) return false;
|
|
10027
|
+
level = level[part];
|
|
10028
|
+
}
|
|
10029
|
+
return true;
|
|
10030
|
+
};
|
|
10031
|
+
const splitName = (name, depth) => {
|
|
10032
|
+
if (depth === 0) {
|
|
10033
|
+
const start2 = name.indexOf("[", 1);
|
|
10034
|
+
return start2 === -1 ? [name, ""] : [name.slice(0, start2), name.slice(start2)];
|
|
10035
|
+
}
|
|
10036
|
+
if (name.startsWith("[]")) return ["[]", name.slice(2)];
|
|
10037
|
+
const close = name.indexOf("]", 1);
|
|
10038
|
+
return name.startsWith("[") && close !== -1 ? [name.slice(1, close), name.slice(close + 1)] : [name, ""];
|
|
10039
|
+
};
|
|
10040
|
+
const putListItem = (items, after, value, depth) => {
|
|
10041
|
+
let childKey = after[2] === "[" && after.endsWith("]") ? after.slice(3, -1) : "";
|
|
10042
|
+
if (childKey === "" || childKey.includes("[") || childKey.includes("]")) childKey = after.slice(2);
|
|
10043
|
+
const last = items[items.length - 1];
|
|
10044
|
+
if (isNode(last) && !hasKey(last, childKey)) put2(last, childKey, value, depth + 1);
|
|
10045
|
+
else items.push(put2(node(), childKey, value, depth + 1));
|
|
10046
|
+
};
|
|
10047
|
+
const put2 = (params, name, value, depth) => {
|
|
10048
|
+
const [key, after] = splitName(name, depth);
|
|
10049
|
+
if (key === "") return void 0;
|
|
10050
|
+
if (after === "" && key === "[]" && depth !== 0) return [value];
|
|
10051
|
+
if (after === "") Reflect.set(params, key, value);
|
|
10052
|
+
else if (after === "[") Reflect.set(params, name, value);
|
|
10053
|
+
else if (after === "[]") list(params, key).push(value);
|
|
10054
|
+
else if (after.startsWith("[]")) putListItem(list(params, key), after, value, depth);
|
|
10055
|
+
else put2(object(params, key), after, value, depth + 1);
|
|
10056
|
+
return params;
|
|
10057
|
+
};
|
|
10058
|
+
const tooDeep = (name) => {
|
|
10059
|
+
let count = 0;
|
|
10060
|
+
for (let at = name.indexOf("["); at !== -1; at = name.indexOf("[", at + 1)) {
|
|
10061
|
+
if (++count >= FORM_DEPTH_LIMIT) return true;
|
|
10062
|
+
}
|
|
10063
|
+
return false;
|
|
10064
|
+
};
|
|
10065
|
+
const fields = node();
|
|
10066
|
+
for (const [name, value] of kept) {
|
|
10067
|
+
if (name.length > FORM_NAME_LIMIT || tooDeep(name)) {
|
|
10068
|
+
Reflect.set(fields, name, value);
|
|
10069
|
+
continue;
|
|
10070
|
+
}
|
|
10071
|
+
try {
|
|
10072
|
+
put2(fields, name, value, 0);
|
|
10073
|
+
} catch (error2) {
|
|
10074
|
+
if (!(error2 instanceof RangeError)) throw error2;
|
|
10075
|
+
Reflect.set(fields, name, value);
|
|
10076
|
+
}
|
|
10077
|
+
}
|
|
10078
|
+
return fields;
|
|
9782
10079
|
}
|
|
9783
|
-
var DEFAULT_MAX_UPLOAD_SIZE, warnedLimits, PayloadTooLargeError;
|
|
10080
|
+
var DEFAULT_MAX_UPLOAD_SIZE, warnedLimits, PayloadTooLargeError, FORM_DEPTH_LIMIT, FORM_FIELD_LIMIT, FORM_NAME_LIMIT, isUnsafeFieldName;
|
|
9784
10081
|
var init_request = __esm({
|
|
9785
10082
|
"../core/src/request.ts"() {
|
|
9786
10083
|
"use strict";
|
|
@@ -9795,6 +10092,10 @@ var init_request = __esm({
|
|
|
9795
10092
|
this.name = "PayloadTooLargeError";
|
|
9796
10093
|
}
|
|
9797
10094
|
};
|
|
10095
|
+
FORM_DEPTH_LIMIT = 32;
|
|
10096
|
+
FORM_FIELD_LIMIT = 4096;
|
|
10097
|
+
FORM_NAME_LIMIT = 1024;
|
|
10098
|
+
isUnsafeFieldName = (key) => key === "__proto__" || key === "constructor" || key === "prototype";
|
|
9798
10099
|
}
|
|
9799
10100
|
});
|
|
9800
10101
|
|
|
@@ -10301,7 +10602,7 @@ var init_routeDiscovery = __esm({
|
|
|
10301
10602
|
|
|
10302
10603
|
// ../core/src/portTakeover.ts
|
|
10303
10604
|
import { execFileSync } from "node:child_process";
|
|
10304
|
-
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, unlinkSync as
|
|
10605
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "node:fs";
|
|
10305
10606
|
import { join as join8 } from "node:path";
|
|
10306
10607
|
function isTruthy2(value) {
|
|
10307
10608
|
return ["true", "1", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
|
|
@@ -10355,7 +10656,7 @@ function readPidfile(port, baseDir) {
|
|
|
10355
10656
|
}
|
|
10356
10657
|
function removePidfile(port, baseDir) {
|
|
10357
10658
|
try {
|
|
10358
|
-
|
|
10659
|
+
unlinkSync4(pidfilePath(port, baseDir));
|
|
10359
10660
|
} catch {
|
|
10360
10661
|
}
|
|
10361
10662
|
}
|
|
@@ -10456,21 +10757,6 @@ var init_portTakeover = __esm({
|
|
|
10456
10757
|
}
|
|
10457
10758
|
});
|
|
10458
10759
|
|
|
10459
|
-
// ../core/src/etag.ts
|
|
10460
|
-
function etagMatches(ifNoneMatch, etag) {
|
|
10461
|
-
const strip = (tag) => tag.trim().replace(/^W\//, "");
|
|
10462
|
-
const target = strip(etag);
|
|
10463
|
-
return ifNoneMatch.split(",").some((candidate) => {
|
|
10464
|
-
const trimmed = candidate.trim();
|
|
10465
|
-
return trimmed === "*" || strip(trimmed) === target;
|
|
10466
|
-
});
|
|
10467
|
-
}
|
|
10468
|
-
var init_etag = __esm({
|
|
10469
|
-
"../core/src/etag.ts"() {
|
|
10470
|
-
"use strict";
|
|
10471
|
-
}
|
|
10472
|
-
});
|
|
10473
|
-
|
|
10474
10760
|
// ../core/src/sessionHandlers/syncBridge.ts
|
|
10475
10761
|
import { Worker } from "node:worker_threads";
|
|
10476
10762
|
function getBridge(key, workerSource, workerData) {
|
|
@@ -11839,14 +12125,18 @@ __export(session_exports, {
|
|
|
11839
12125
|
Session: () => Session,
|
|
11840
12126
|
VALID_SESSION_BACKENDS: () => VALID_SESSION_BACKENDS,
|
|
11841
12127
|
buildSessionCookie: () => buildSessionCookie,
|
|
12128
|
+
currentFormTokenSessionId: () => currentFormTokenSessionId,
|
|
11842
12129
|
isSecureScheme: () => isSecureScheme,
|
|
11843
12130
|
isValidSessionId: () => isValidSessionId,
|
|
12131
|
+
runWithFormTokenSession: () => runWithFormTokenSession,
|
|
11844
12132
|
sessionCookieName: () => sessionCookieName,
|
|
11845
12133
|
sessionCookieToSend: () => sessionCookieToSend,
|
|
11846
|
-
sessionStrictMode: () => sessionStrictMode
|
|
12134
|
+
sessionStrictMode: () => sessionStrictMode,
|
|
12135
|
+
setFormTokenSession: () => setFormTokenSession
|
|
11847
12136
|
});
|
|
12137
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
|
|
11848
12138
|
import { createHash as createHash5, randomBytes as randomBytes4 } from "node:crypto";
|
|
11849
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as
|
|
12139
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "node:fs";
|
|
11850
12140
|
import { join as join9 } from "node:path";
|
|
11851
12141
|
function isValidSessionId(sessionId) {
|
|
11852
12142
|
return typeof sessionId === "string" && SESSION_ID_PATTERN.test(sessionId);
|
|
@@ -11912,7 +12202,21 @@ function sessionCookieToSend(sess, existingSid, rawReq, rawRes) {
|
|
|
11912
12202
|
const socketEncrypted = rawReq.socket?.encrypted === true;
|
|
11913
12203
|
return buildSessionCookie(newSid, ttl, void 0, forwardedProto, socketEncrypted);
|
|
11914
12204
|
}
|
|
11915
|
-
|
|
12205
|
+
function runWithFormTokenSession(fn) {
|
|
12206
|
+
return sessionStore.run({ session: null }, fn);
|
|
12207
|
+
}
|
|
12208
|
+
function setFormTokenSession(session) {
|
|
12209
|
+
const store = sessionStore.getStore();
|
|
12210
|
+
if (store) store.session = session;
|
|
12211
|
+
}
|
|
12212
|
+
function currentFormTokenSessionId() {
|
|
12213
|
+
const session = sessionStore.getStore()?.session;
|
|
12214
|
+
const sid = session?.getSessionId();
|
|
12215
|
+
if (!session || !sid) return "";
|
|
12216
|
+
if (session.isFresh()) session.set("_tina4_csrf", true);
|
|
12217
|
+
return sid;
|
|
12218
|
+
}
|
|
12219
|
+
var SESSION_ID_PATTERN, VALID_SESSION_BACKENDS, CANONICAL_SESSION_BACKENDS, RETIRED_SESSION_BACKENDS, FileSessionHandler, RedisSessionHandler, FLASH_PREFIX, Session, sessionStore;
|
|
11916
12220
|
var init_session = __esm({
|
|
11917
12221
|
"../core/src/session.ts"() {
|
|
11918
12222
|
"use strict";
|
|
@@ -11996,7 +12300,7 @@ var init_session = __esm({
|
|
|
11996
12300
|
const wrapper = JSON.parse(raw);
|
|
11997
12301
|
if (wrapper._expires && wrapper._expires > 0 && Date.now() / 1e3 > wrapper._expires) {
|
|
11998
12302
|
try {
|
|
11999
|
-
|
|
12303
|
+
unlinkSync5(filePath);
|
|
12000
12304
|
} catch {
|
|
12001
12305
|
}
|
|
12002
12306
|
return null;
|
|
@@ -12015,7 +12319,7 @@ var init_session = __esm({
|
|
|
12015
12319
|
destroy(sessionId) {
|
|
12016
12320
|
const filePath = this.filePath(sessionId);
|
|
12017
12321
|
try {
|
|
12018
|
-
if (existsSync8(filePath))
|
|
12322
|
+
if (existsSync8(filePath)) unlinkSync5(filePath);
|
|
12019
12323
|
} catch {
|
|
12020
12324
|
}
|
|
12021
12325
|
}
|
|
@@ -12031,11 +12335,11 @@ var init_session = __esm({
|
|
|
12031
12335
|
const raw = readFileSync6(fullPath, "utf-8");
|
|
12032
12336
|
const wrapper = JSON.parse(raw);
|
|
12033
12337
|
if (wrapper._expires && wrapper._expires > 0 && now > wrapper._expires) {
|
|
12034
|
-
|
|
12338
|
+
unlinkSync5(fullPath);
|
|
12035
12339
|
}
|
|
12036
12340
|
} catch {
|
|
12037
12341
|
try {
|
|
12038
|
-
|
|
12342
|
+
unlinkSync5(fullPath);
|
|
12039
12343
|
} catch {
|
|
12040
12344
|
}
|
|
12041
12345
|
}
|
|
@@ -12555,6 +12859,22 @@ var init_session = __esm({
|
|
|
12555
12859
|
this.loaded = /* @__PURE__ */ new Map();
|
|
12556
12860
|
}
|
|
12557
12861
|
};
|
|
12862
|
+
sessionStore = new AsyncLocalStorage3();
|
|
12863
|
+
}
|
|
12864
|
+
});
|
|
12865
|
+
|
|
12866
|
+
// ../core/src/etag.ts
|
|
12867
|
+
function etagMatches(ifNoneMatch, etag) {
|
|
12868
|
+
const strip = (tag) => tag.trim().replace(/^W\//, "");
|
|
12869
|
+
const target = strip(etag);
|
|
12870
|
+
return ifNoneMatch.split(",").some((candidate) => {
|
|
12871
|
+
const trimmed = candidate.trim();
|
|
12872
|
+
return trimmed === "*" || strip(trimmed) === target;
|
|
12873
|
+
});
|
|
12874
|
+
}
|
|
12875
|
+
var init_etag = __esm({
|
|
12876
|
+
"../core/src/etag.ts"() {
|
|
12877
|
+
"use strict";
|
|
12558
12878
|
}
|
|
12559
12879
|
});
|
|
12560
12880
|
|
|
@@ -12708,6 +13028,7 @@ async function sessionAutoStart(rawReq, rawRes, req2) {
|
|
|
12708
13028
|
return;
|
|
12709
13029
|
}
|
|
12710
13030
|
req2.session = sess;
|
|
13031
|
+
setFormTokenSession(sess);
|
|
12711
13032
|
const origEnd = rawRes.end.bind(rawRes);
|
|
12712
13033
|
let finalised = false;
|
|
12713
13034
|
rawRes.end = function(...args) {
|
|
@@ -12736,6 +13057,7 @@ var _resetRequestCaches, COMPRESSIBLE_PREFIXES;
|
|
|
12736
13057
|
var init_dispatchPipeline = __esm({
|
|
12737
13058
|
"../core/src/dispatchPipeline.ts"() {
|
|
12738
13059
|
"use strict";
|
|
13060
|
+
init_session();
|
|
12739
13061
|
init_logger();
|
|
12740
13062
|
init_etag();
|
|
12741
13063
|
COMPRESSIBLE_PREFIXES = [
|
|
@@ -12885,14 +13207,21 @@ function parseStack(stack) {
|
|
|
12885
13207
|
const frames = [];
|
|
12886
13208
|
const lines = stack.split("\n");
|
|
12887
13209
|
for (const line of lines) {
|
|
12888
|
-
|
|
12889
|
-
if (
|
|
12890
|
-
|
|
12891
|
-
|
|
13210
|
+
const t = line.trim();
|
|
13211
|
+
if (!t.startsWith("at ")) continue;
|
|
13212
|
+
const rest = t.slice(3);
|
|
13213
|
+
let func = "{anonymous}";
|
|
13214
|
+
let loc = rest;
|
|
13215
|
+
if (rest.endsWith(")")) {
|
|
13216
|
+
const open = rest.lastIndexOf(" (");
|
|
13217
|
+
if (open >= 0) {
|
|
13218
|
+
func = rest.slice(0, open);
|
|
13219
|
+
loc = rest.slice(open + 2, -1);
|
|
13220
|
+
}
|
|
12892
13221
|
}
|
|
12893
|
-
|
|
12894
|
-
if (
|
|
12895
|
-
frames.push({ func
|
|
13222
|
+
const lc = loc.match(/:(\d+):(\d+)$/);
|
|
13223
|
+
if (lc && lc.index !== void 0) {
|
|
13224
|
+
frames.push({ func, file: loc.slice(0, lc.index), line: parseInt(lc[1], 10), column: parseInt(lc[2], 10) });
|
|
12896
13225
|
}
|
|
12897
13226
|
}
|
|
12898
13227
|
return frames;
|
|
@@ -13217,7 +13546,7 @@ var init_rateLimiter = __esm({
|
|
|
13217
13546
|
});
|
|
13218
13547
|
|
|
13219
13548
|
// ../core/src/devMailbox.ts
|
|
13220
|
-
import { mkdirSync as mkdirSync7, readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync6, unlinkSync as
|
|
13549
|
+
import { mkdirSync as mkdirSync7, readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync6, unlinkSync as unlinkSync6, existsSync as existsSync9 } from "node:fs";
|
|
13221
13550
|
import { join as join11 } from "node:path";
|
|
13222
13551
|
import { randomUUID } from "node:crypto";
|
|
13223
13552
|
var DevMailbox;
|
|
@@ -13349,7 +13678,7 @@ var init_devMailbox = __esm({
|
|
|
13349
13678
|
const filePath = join11(this.mailboxDir, folder, `${msgId}.json`);
|
|
13350
13679
|
if (existsSync9(filePath)) {
|
|
13351
13680
|
try {
|
|
13352
|
-
|
|
13681
|
+
unlinkSync6(filePath);
|
|
13353
13682
|
deleted = true;
|
|
13354
13683
|
} catch {
|
|
13355
13684
|
}
|
|
@@ -13368,7 +13697,7 @@ var init_devMailbox = __esm({
|
|
|
13368
13697
|
if (existsSync9(dir)) {
|
|
13369
13698
|
const files = readdirSync4(dir).filter((file) => file.endsWith(".json"));
|
|
13370
13699
|
for (const file of files) {
|
|
13371
|
-
|
|
13700
|
+
unlinkSync6(join11(dir, file));
|
|
13372
13701
|
}
|
|
13373
13702
|
}
|
|
13374
13703
|
} catch {
|
|
@@ -13512,7 +13841,7 @@ function enginePath() {
|
|
|
13512
13841
|
function runEngine(target) {
|
|
13513
13842
|
const binary = enginePath();
|
|
13514
13843
|
if (!binary) throw new MetricsEngineError(`tina4 not found on PATH - ${INSTALL_HINT}`);
|
|
13515
|
-
const processResult = spawnSync(binary, ["metrics", "--path", target, "--json"], {
|
|
13844
|
+
const processResult = spawnSync(binary, ["metrics", "--path", target, "--json", "--no-history"], {
|
|
13516
13845
|
encoding: "utf8",
|
|
13517
13846
|
timeout: 6e4,
|
|
13518
13847
|
maxBuffer: 64 * 1024 * 1024
|
|
@@ -16173,21 +16502,45 @@ function safePath(projectRoot3, relPath) {
|
|
|
16173
16502
|
if (resolved !== projectRoot3 && !resolved.startsWith(rootPrefix)) {
|
|
16174
16503
|
throw new Error(`Path escapes project directory: ${relPath}`);
|
|
16175
16504
|
}
|
|
16176
|
-
let
|
|
16505
|
+
let realRoot;
|
|
16177
16506
|
try {
|
|
16178
|
-
|
|
16507
|
+
realRoot = fs4.realpathSync(projectRoot3);
|
|
16179
16508
|
} catch {
|
|
16180
|
-
|
|
16509
|
+
return resolved;
|
|
16181
16510
|
}
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
|
|
16185
|
-
|
|
16186
|
-
throw new Error(`Path escapes project directory: ${relPath}`);
|
|
16187
|
-
}
|
|
16511
|
+
const real = realPathOf(resolved);
|
|
16512
|
+
const realPrefix = realRoot.endsWith(path3.sep) ? realRoot : realRoot + path3.sep;
|
|
16513
|
+
if (real === null || real !== realRoot && !real.startsWith(realPrefix)) {
|
|
16514
|
+
throw new Error(`Path escapes project directory: ${relPath}`);
|
|
16188
16515
|
}
|
|
16189
16516
|
return resolved;
|
|
16190
16517
|
}
|
|
16518
|
+
function realPathOf(p, hops = 0) {
|
|
16519
|
+
let probe = p;
|
|
16520
|
+
while (!fs4.existsSync(probe) && !isSymlink(probe) && path3.dirname(probe) !== probe) {
|
|
16521
|
+
probe = path3.dirname(probe);
|
|
16522
|
+
}
|
|
16523
|
+
try {
|
|
16524
|
+
return fs4.realpathSync.native(probe);
|
|
16525
|
+
} catch {
|
|
16526
|
+
if (hops >= 40) return null;
|
|
16527
|
+
let target;
|
|
16528
|
+
try {
|
|
16529
|
+
target = fs4.readlinkSync(probe);
|
|
16530
|
+
} catch {
|
|
16531
|
+
return null;
|
|
16532
|
+
}
|
|
16533
|
+
const base = path3.isAbsolute(target) ? target : path3.dirname(probe) + path3.sep + target;
|
|
16534
|
+
return realPathOf(base + p.slice(probe.length), hops + 1);
|
|
16535
|
+
}
|
|
16536
|
+
}
|
|
16537
|
+
function isSymlink(p) {
|
|
16538
|
+
try {
|
|
16539
|
+
return fs4.lstatSync(p).isSymbolicLink();
|
|
16540
|
+
} catch {
|
|
16541
|
+
return false;
|
|
16542
|
+
}
|
|
16543
|
+
}
|
|
16191
16544
|
function redactEnv(key, value) {
|
|
16192
16545
|
const sensitive = ["secret", "password", "token", "key", "credential", "api_key"];
|
|
16193
16546
|
if (sensitive.some((s) => key.toLowerCase().includes(s))) {
|
|
@@ -17032,8 +17385,10 @@ function registerDevTools(server) {
|
|
|
17032
17385
|
return { error: `Invalid path ${JSON.stringify(rel)}: ${proseErr}` };
|
|
17033
17386
|
}
|
|
17034
17387
|
rel = normalizeCoderPath(projectRoot4, rel);
|
|
17035
|
-
|
|
17036
|
-
|
|
17388
|
+
let resolved;
|
|
17389
|
+
try {
|
|
17390
|
+
resolved = safePath(projectRoot4, rel);
|
|
17391
|
+
} catch {
|
|
17037
17392
|
return { error: `Path escapes project directory: ${rel}` };
|
|
17038
17393
|
}
|
|
17039
17394
|
if (!fs4.existsSync(resolved) || !fs4.statSync(resolved).isFile()) {
|
|
@@ -17330,6 +17685,7 @@ var req, PARSE_ERROR, INVALID_REQUEST, METHOD_NOT_FOUND, INVALID_PARAMS, INTERNA
|
|
|
17330
17685
|
var init_mcp = __esm({
|
|
17331
17686
|
"../core/src/mcp.ts"() {
|
|
17332
17687
|
"use strict";
|
|
17688
|
+
init_slashTrim();
|
|
17333
17689
|
init_version();
|
|
17334
17690
|
req = createRequire2(import.meta.url);
|
|
17335
17691
|
PARSE_ERROR = -32700;
|
|
@@ -17356,7 +17712,7 @@ var init_mcp = __esm({
|
|
|
17356
17712
|
// threaded, so this in-process map is the whole coordination mechanism.
|
|
17357
17713
|
_sseChannels = /* @__PURE__ */ new Map();
|
|
17358
17714
|
constructor(mcpPath, name = "Tina4 MCP", version = "1.0.0") {
|
|
17359
|
-
this.path = mcpPath
|
|
17715
|
+
this.path = rtrimSlashes(mcpPath);
|
|
17360
17716
|
this.name = name;
|
|
17361
17717
|
this.version = version;
|
|
17362
17718
|
_McpServer._instances.push(this);
|
|
@@ -21140,7 +21496,7 @@ var init_job = __esm({
|
|
|
21140
21496
|
});
|
|
21141
21497
|
|
|
21142
21498
|
// ../core/src/queueBackends/liteBackend.ts
|
|
21143
|
-
import { mkdirSync as mkdirSync11, readdirSync as readdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync9, unlinkSync as
|
|
21499
|
+
import { mkdirSync as mkdirSync11, readdirSync as readdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync9, unlinkSync as unlinkSync7, existsSync as existsSync16 } from "node:fs";
|
|
21144
21500
|
import { join as join18 } from "node:path";
|
|
21145
21501
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
21146
21502
|
var LiteBackend;
|
|
@@ -21310,7 +21666,7 @@ var init_liteBackend = __esm({
|
|
|
21310
21666
|
}
|
|
21311
21667
|
if (record.availableAt && record.availableAt > now) continue;
|
|
21312
21668
|
try {
|
|
21313
|
-
|
|
21669
|
+
unlinkSync7(filePath);
|
|
21314
21670
|
} catch {
|
|
21315
21671
|
continue;
|
|
21316
21672
|
}
|
|
@@ -21344,10 +21700,10 @@ var init_liteBackend = __esm({
|
|
|
21344
21700
|
job.priority = job.priority ?? 0;
|
|
21345
21701
|
this.writeReserved(queue, job);
|
|
21346
21702
|
try {
|
|
21347
|
-
|
|
21703
|
+
unlinkSync7(filePath);
|
|
21348
21704
|
} catch {
|
|
21349
21705
|
try {
|
|
21350
|
-
|
|
21706
|
+
unlinkSync7(this.reservedPath(queue, job.id));
|
|
21351
21707
|
} catch {
|
|
21352
21708
|
}
|
|
21353
21709
|
continue;
|
|
@@ -21369,10 +21725,10 @@ var init_liteBackend = __esm({
|
|
|
21369
21725
|
job.priority = job.priority ?? 0;
|
|
21370
21726
|
this.writeReserved(queue, job);
|
|
21371
21727
|
try {
|
|
21372
|
-
|
|
21728
|
+
unlinkSync7(filePath);
|
|
21373
21729
|
} catch {
|
|
21374
21730
|
try {
|
|
21375
|
-
|
|
21731
|
+
unlinkSync7(this.reservedPath(queue, job.id));
|
|
21376
21732
|
} catch {
|
|
21377
21733
|
}
|
|
21378
21734
|
continue;
|
|
@@ -21387,7 +21743,7 @@ var init_liteBackend = __esm({
|
|
|
21387
21743
|
*/
|
|
21388
21744
|
clearReservation(queue, jobId) {
|
|
21389
21745
|
try {
|
|
21390
|
-
|
|
21746
|
+
unlinkSync7(this.reservedPath(queue, jobId));
|
|
21391
21747
|
} catch {
|
|
21392
21748
|
}
|
|
21393
21749
|
}
|
|
@@ -21430,7 +21786,7 @@ var init_liteBackend = __esm({
|
|
|
21430
21786
|
try {
|
|
21431
21787
|
const files = readdirSync9(dir).filter((f) => f.endsWith(".queue-data"));
|
|
21432
21788
|
for (const file of files) {
|
|
21433
|
-
|
|
21789
|
+
unlinkSync7(join18(dir, file));
|
|
21434
21790
|
count++;
|
|
21435
21791
|
}
|
|
21436
21792
|
} catch {
|
|
@@ -21440,7 +21796,7 @@ var init_liteBackend = __esm({
|
|
|
21440
21796
|
if (existsSync16(failedDir)) {
|
|
21441
21797
|
const files = readdirSync9(failedDir).filter((f) => f.endsWith(".queue-data"));
|
|
21442
21798
|
for (const file of files) {
|
|
21443
|
-
|
|
21799
|
+
unlinkSync7(join18(failedDir, file));
|
|
21444
21800
|
count++;
|
|
21445
21801
|
}
|
|
21446
21802
|
}
|
|
@@ -21451,7 +21807,7 @@ var init_liteBackend = __esm({
|
|
|
21451
21807
|
if (existsSync16(reservedDir)) {
|
|
21452
21808
|
const files = readdirSync9(reservedDir).filter((f) => f.endsWith(".queue-data"));
|
|
21453
21809
|
for (const file of files) {
|
|
21454
|
-
|
|
21810
|
+
unlinkSync7(join18(reservedDir, file));
|
|
21455
21811
|
count++;
|
|
21456
21812
|
}
|
|
21457
21813
|
}
|
|
@@ -21509,7 +21865,7 @@ var init_liteBackend = __esm({
|
|
|
21509
21865
|
const prefix = this.nextPrefix();
|
|
21510
21866
|
const queueDir = join18(this.basePath, q);
|
|
21511
21867
|
writeFileSync9(join18(queueDir, `${prefix}_${jobId}.queue-data`), JSON.stringify(job, null, 2));
|
|
21512
|
-
|
|
21868
|
+
unlinkSync7(filePath);
|
|
21513
21869
|
return true;
|
|
21514
21870
|
}
|
|
21515
21871
|
}
|
|
@@ -21545,7 +21901,7 @@ var init_liteBackend = __esm({
|
|
|
21545
21901
|
const files = readdirSync9(failedDir).filter((f) => f.endsWith(".queue-data"));
|
|
21546
21902
|
for (const file of files) {
|
|
21547
21903
|
try {
|
|
21548
|
-
|
|
21904
|
+
unlinkSync7(join18(failedDir, file));
|
|
21549
21905
|
count++;
|
|
21550
21906
|
} catch {
|
|
21551
21907
|
}
|
|
@@ -21560,7 +21916,7 @@ var init_liteBackend = __esm({
|
|
|
21560
21916
|
try {
|
|
21561
21917
|
const job = JSON.parse(readFileSync14(join18(dir, file), "utf-8"));
|
|
21562
21918
|
if (job.status === status2) {
|
|
21563
|
-
|
|
21919
|
+
unlinkSync7(join18(dir, file));
|
|
21564
21920
|
count++;
|
|
21565
21921
|
}
|
|
21566
21922
|
} catch {
|
|
@@ -21595,7 +21951,7 @@ var init_liteBackend = __esm({
|
|
|
21595
21951
|
job.delayUntil = null;
|
|
21596
21952
|
const prefix = this.nextPrefix();
|
|
21597
21953
|
writeFileSync9(join18(queueDir, `${prefix}_${job.id}.queue-data`), JSON.stringify(job, null, 2));
|
|
21598
|
-
|
|
21954
|
+
unlinkSync7(filePath);
|
|
21599
21955
|
count++;
|
|
21600
21956
|
} catch {
|
|
21601
21957
|
}
|
|
@@ -21627,10 +21983,10 @@ var init_liteBackend = __esm({
|
|
|
21627
21983
|
job.priority = job.priority ?? 0;
|
|
21628
21984
|
this.writeReserved(queue, job);
|
|
21629
21985
|
try {
|
|
21630
|
-
|
|
21986
|
+
unlinkSync7(filePath);
|
|
21631
21987
|
} catch {
|
|
21632
21988
|
try {
|
|
21633
|
-
|
|
21989
|
+
unlinkSync7(this.reservedPath(queue, job.id));
|
|
21634
21990
|
} catch {
|
|
21635
21991
|
}
|
|
21636
21992
|
continue;
|
|
@@ -21732,7 +22088,7 @@ var init_liteBackend = __esm({
|
|
|
21732
22088
|
retryJob(queue, job, delaySeconds) {
|
|
21733
22089
|
this.clearReservation(queue, job.id);
|
|
21734
22090
|
try {
|
|
21735
|
-
|
|
22091
|
+
unlinkSync7(join18(this.ensureFailedDir(queue), `${job.id}.queue-data`));
|
|
21736
22092
|
} catch {
|
|
21737
22093
|
}
|
|
21738
22094
|
job.attempts = (job.attempts || 0) + 1;
|
|
@@ -22282,7 +22638,8 @@ function archiveDir() {
|
|
|
22282
22638
|
return p;
|
|
22283
22639
|
}
|
|
22284
22640
|
function slugify(title) {
|
|
22285
|
-
const
|
|
22641
|
+
const dashed = (title || "").trim().toLowerCase().replace(/[^A-Za-z0-9_-]+/g, "-");
|
|
22642
|
+
const slug = trimChar(dashed, 45);
|
|
22286
22643
|
return slug.slice(0, 80) || `plan-${Math.floor(Date.now() / 1e3)}`;
|
|
22287
22644
|
}
|
|
22288
22645
|
function parse(text) {
|
|
@@ -22419,6 +22776,7 @@ var PLAN_DIR, CURRENT_FILE, ARCHIVE_SUBDIR, STEP_RE, Plan;
|
|
|
22419
22776
|
var init_plan = __esm({
|
|
22420
22777
|
"../core/src/plan.ts"() {
|
|
22421
22778
|
"use strict";
|
|
22779
|
+
init_slashTrim();
|
|
22422
22780
|
PLAN_DIR = "plan";
|
|
22423
22781
|
CURRENT_FILE = ".current";
|
|
22424
22782
|
ARCHIVE_SUBDIR = "done";
|
|
@@ -22690,7 +23048,7 @@ var init_plan = __esm({
|
|
|
22690
23048
|
const existing = (currentPlan.steps || []).map((s) => s.text || "");
|
|
22691
23049
|
const title = currentPlan.title || target;
|
|
22692
23050
|
const goal = currentPlan.goal || "";
|
|
22693
|
-
const aiUrl = process.env.TINA4_AI_URL || "http://localhost:
|
|
23051
|
+
const aiUrl = process.env.TINA4_AI_URL || "http://localhost:11435/api/chat";
|
|
22694
23052
|
const aiModel = process.env.TINA4_AI_MODEL || "qwen2.5-coder:14b";
|
|
22695
23053
|
const prompts = fleshPrompt(title, goal, existing, prompt);
|
|
22696
23054
|
const fetched = await fetchFleshReply(aiUrl, aiModel, prompts);
|
|
@@ -23658,7 +24016,7 @@ function relativePath(absPath, projectRoot3, frameworkRoots) {
|
|
|
23658
24016
|
}
|
|
23659
24017
|
function tokenise(text) {
|
|
23660
24018
|
if (!text) return [];
|
|
23661
|
-
const expanded = text.replace(
|
|
24019
|
+
const expanded = text.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])(?=[A-Z][a-z])/g, "$1 ").toLowerCase();
|
|
23662
24020
|
return expanded.split(/[\s_\-./:,;()\[\]{}\\]+/).filter((p) => p.length > 0);
|
|
23663
24021
|
}
|
|
23664
24022
|
function summariseDoc(doc) {
|
|
@@ -23889,7 +24247,7 @@ function returnFromSignature(signature) {
|
|
|
23889
24247
|
const rest = signature.slice(close + 1).trim();
|
|
23890
24248
|
return rest.startsWith(":") ? rest.slice(1).trim() : "";
|
|
23891
24249
|
}
|
|
23892
|
-
var STDLIB_ALLOWLIST, _DOCS_CACHE, __FILENAME, __DIRNAME, USER_DIRS,
|
|
24250
|
+
var STDLIB_ALLOWLIST, _DOCS_CACHE, __FILENAME, __DIRNAME, USER_DIRS, FENCE_RE, CALL_RE, sharedFrameworkIndex, Docs;
|
|
23893
24251
|
var init_docs = __esm({
|
|
23894
24252
|
"../core/src/docs.ts"() {
|
|
23895
24253
|
"use strict";
|
|
@@ -24028,7 +24386,6 @@ var init_docs = __esm({
|
|
|
24028
24386
|
__FILENAME = fileURLToPath5(import.meta.url);
|
|
24029
24387
|
__DIRNAME = path8.dirname(__FILENAME);
|
|
24030
24388
|
USER_DIRS = ["src/orm", "src/routes", "src/app", "src/services", "src/models"];
|
|
24031
|
-
CAMEL_SPLIT = /([a-z0-9])([A-Z])|([A-Z]+)([A-Z][a-z])/g;
|
|
24032
24389
|
FENCE_RE = new RegExp(
|
|
24033
24390
|
String.fromCharCode(96, 96, 96) + "[a-zA-Z0-9_+-]*\\n([\\s\\S]*?)\\n" + String.fromCharCode(96, 96, 96),
|
|
24034
24391
|
"g"
|
|
@@ -24402,7 +24759,7 @@ ${end}
|
|
|
24402
24759
|
|
|
24403
24760
|
// ../core/src/devAdmin.ts
|
|
24404
24761
|
import { cpus as osCpus } from "node:os";
|
|
24405
|
-
import { readFileSync as readFileSync18, writeFileSync as writeFileSync13, existsSync as existsSync20, readdirSync as readdirSync13, mkdirSync as mkdirSync14, copyFileSync as copyFileSync2, statSync as statSync15, openSync as
|
|
24762
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync13, existsSync as existsSync20, readdirSync as readdirSync13, mkdirSync as mkdirSync14, copyFileSync as copyFileSync2, statSync as statSync15, openSync as openSync5, closeSync as closeSync5, fstatSync as fstatSync3, fchmodSync as fchmodSync2, ftruncateSync as ftruncateSync2, constants as constants4, realpathSync as realpathSync6, lstatSync as lstatSync3 } from "node:fs";
|
|
24406
24763
|
import { join as join22, dirname as dirname11, resolve as resolve12, relative as relative9, sep as sep6, basename as basename5 } from "node:path";
|
|
24407
24764
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
24408
24765
|
import { timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
@@ -24601,8 +24958,41 @@ function quoteTableRef(name) {
|
|
|
24601
24958
|
}
|
|
24602
24959
|
return parts.map((p) => `"${p}"`).join(".");
|
|
24603
24960
|
}
|
|
24961
|
+
function parseSeedRequest(url, body) {
|
|
24962
|
+
const b = body ?? {};
|
|
24963
|
+
const table2 = String(url.searchParams.get("table") ?? b.table ?? "");
|
|
24964
|
+
const count = parseInt(String(url.searchParams.get("count") ?? b.count ?? "10"), 10) || 10;
|
|
24965
|
+
const seedRaw = url.searchParams.get("seed") ?? b.seed;
|
|
24966
|
+
const seed = seedRaw !== void 0 && seedRaw !== null && String(seedRaw) !== "" ? Number.isNaN(parseInt(String(seedRaw), 10)) ? void 0 : parseInt(String(seedRaw), 10) : void 0;
|
|
24967
|
+
const clear = String(url.searchParams.get("clear") ?? b.clear ?? "") === "true" || b.clear === true;
|
|
24968
|
+
const strict = String(url.searchParams.get("strict") ?? b.strict ?? "") === "true" || b.strict === true;
|
|
24969
|
+
return { table: table2, count, seed, clear, strict };
|
|
24970
|
+
}
|
|
24971
|
+
function buildSeedFieldMap(columns, fake) {
|
|
24972
|
+
const rules = [
|
|
24973
|
+
[(n) => n.includes("email"), () => fake.email()],
|
|
24974
|
+
[(n) => n.includes("name"), () => fake.name()],
|
|
24975
|
+
[(n) => n.includes("phone"), () => fake.phone()],
|
|
24976
|
+
[(n) => n.includes("address") || n.includes("city") || n.includes("country"), () => fake.address()],
|
|
24977
|
+
[(n) => n.includes("url") || n.includes("website"), () => fake.url()],
|
|
24978
|
+
[(_n, t) => t.includes("int"), () => fake.integer(1, 1e3)],
|
|
24979
|
+
[(_n, t) => /real|float|double|numeric|decimal/.test(t), () => fake.numeric(0, 1e3, 2)],
|
|
24980
|
+
[(_n, t) => t.includes("bool"), () => fake.boolean()],
|
|
24981
|
+
[(_n, t) => t.includes("date") || t.includes("time"), () => fake.date()]
|
|
24982
|
+
];
|
|
24983
|
+
const sentence = () => fake.sentence(3);
|
|
24984
|
+
const fieldMap = {};
|
|
24985
|
+
for (const col of columns) {
|
|
24986
|
+
if (col.name.toLowerCase() === "id" || col.primaryKey === true) continue;
|
|
24987
|
+
const name = col.name.toLowerCase();
|
|
24988
|
+
const type2 = col.type.toLowerCase();
|
|
24989
|
+
const rule = rules.find(([matches]) => matches(name, type2));
|
|
24990
|
+
fieldMap[col.name] = rule ? rule[1] : sentence;
|
|
24991
|
+
}
|
|
24992
|
+
return fieldMap;
|
|
24993
|
+
}
|
|
24604
24994
|
function supervisorBaseUrl() {
|
|
24605
|
-
const explicit = (process.env.TINA4_SUPERVISOR_URL ?? "")
|
|
24995
|
+
const explicit = rtrimSlashes(process.env.TINA4_SUPERVISOR_URL ?? "");
|
|
24606
24996
|
if (explicit) return explicit;
|
|
24607
24997
|
const agentPort = (process.env.TINA4_AGENT_PORT ?? "").trim();
|
|
24608
24998
|
if (/^\d+$/.test(agentPort)) return `http://127.0.0.1:${parseInt(agentPort, 10)}`;
|
|
@@ -24610,6 +25000,42 @@ function supervisorBaseUrl() {
|
|
|
24610
25000
|
if (/^\d+$/.test(fwPort)) return `http://127.0.0.1:${parseInt(fwPort, 10) + 2e3}`;
|
|
24611
25001
|
return "http://127.0.0.1:9145";
|
|
24612
25002
|
}
|
|
25003
|
+
function buildSupervisorBody(method, body) {
|
|
25004
|
+
if (method !== "POST" && method !== "PUT" && method !== "PATCH" && method !== "DELETE") return void 0;
|
|
25005
|
+
if (body === void 0 || body === null) return void 0;
|
|
25006
|
+
if (typeof body === "string") return body;
|
|
25007
|
+
if (typeof body !== "object") return void 0;
|
|
25008
|
+
let outBody = body;
|
|
25009
|
+
if (!Array.isArray(body)) {
|
|
25010
|
+
const pf = body.plan_file;
|
|
25011
|
+
if (typeof pf === "string" && pf && !pf.includes("/")) outBody = { ...body, plan_file: `plan/${pf}` };
|
|
25012
|
+
}
|
|
25013
|
+
return JSON.stringify(outBody);
|
|
25014
|
+
}
|
|
25015
|
+
async function streamEventStream(res, upstream, timer) {
|
|
25016
|
+
res.raw.writeHead(upstream.status || 200, {
|
|
25017
|
+
"Content-Type": upstream.headers.get("content-type") ?? "text/event-stream",
|
|
25018
|
+
"Cache-Control": "no-cache",
|
|
25019
|
+
Connection: "keep-alive"
|
|
25020
|
+
});
|
|
25021
|
+
if (typeof res.raw.flushHeaders === "function") res.raw.flushHeaders();
|
|
25022
|
+
if (!upstream.body) {
|
|
25023
|
+
res.raw.end();
|
|
25024
|
+
clearTimeout(timer);
|
|
25025
|
+
return;
|
|
25026
|
+
}
|
|
25027
|
+
const reader = upstream.body.getReader();
|
|
25028
|
+
try {
|
|
25029
|
+
while (true) {
|
|
25030
|
+
const { done, value } = await reader.read();
|
|
25031
|
+
if (done) break;
|
|
25032
|
+
if (value) res.raw.write(Buffer.from(value));
|
|
25033
|
+
}
|
|
25034
|
+
} finally {
|
|
25035
|
+
clearTimeout(timer);
|
|
25036
|
+
res.raw.end();
|
|
25037
|
+
}
|
|
25038
|
+
}
|
|
24613
25039
|
async function proxyToSupervisor(req2, res, downstreamPath) {
|
|
24614
25040
|
const base = supervisorBaseUrl();
|
|
24615
25041
|
let qs = "";
|
|
@@ -24620,24 +25046,7 @@ async function proxyToSupervisor(req2, res, downstreamPath) {
|
|
|
24620
25046
|
}
|
|
24621
25047
|
const target = `${base}${downstreamPath}${qs}`;
|
|
24622
25048
|
const method = (req2.method ?? "GET").toUpperCase();
|
|
24623
|
-
|
|
24624
|
-
if (method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE") {
|
|
24625
|
-
const body = req2.body;
|
|
24626
|
-
if (body !== void 0 && body !== null) {
|
|
24627
|
-
if (typeof body === "string") {
|
|
24628
|
-
bodyText = body;
|
|
24629
|
-
} else if (typeof body === "object") {
|
|
24630
|
-
let outBody = body;
|
|
24631
|
-
if (!Array.isArray(body)) {
|
|
24632
|
-
const pf = body.plan_file;
|
|
24633
|
-
if (typeof pf === "string" && pf && !pf.includes("/")) {
|
|
24634
|
-
outBody = { ...body, plan_file: `plan/${pf}` };
|
|
24635
|
-
}
|
|
24636
|
-
}
|
|
24637
|
-
bodyText = JSON.stringify(outBody);
|
|
24638
|
-
}
|
|
24639
|
-
}
|
|
24640
|
-
}
|
|
25049
|
+
const bodyText = buildSupervisorBody(method, req2.body);
|
|
24641
25050
|
const timeoutMs = downstreamPath === "/execute" || downstreamPath === "/chat" ? 6e5 : 3e4;
|
|
24642
25051
|
const ctrl = new AbortController();
|
|
24643
25052
|
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
@@ -24663,30 +25072,7 @@ async function proxyToSupervisor(req2, res, downstreamPath) {
|
|
|
24663
25072
|
}
|
|
24664
25073
|
const ct = (upstream.headers.get("content-type") ?? "").toLowerCase();
|
|
24665
25074
|
if (ct.includes("text/event-stream")) {
|
|
24666
|
-
res
|
|
24667
|
-
"Content-Type": upstream.headers.get("content-type") ?? "text/event-stream",
|
|
24668
|
-
"Cache-Control": "no-cache",
|
|
24669
|
-
Connection: "keep-alive"
|
|
24670
|
-
});
|
|
24671
|
-
if (typeof res.raw.flushHeaders === "function") {
|
|
24672
|
-
res.raw.flushHeaders();
|
|
24673
|
-
}
|
|
24674
|
-
if (!upstream.body) {
|
|
24675
|
-
res.raw.end();
|
|
24676
|
-
clearTimeout(timer);
|
|
24677
|
-
return;
|
|
24678
|
-
}
|
|
24679
|
-
const reader = upstream.body.getReader();
|
|
24680
|
-
try {
|
|
24681
|
-
while (true) {
|
|
24682
|
-
const { done, value } = await reader.read();
|
|
24683
|
-
if (done) break;
|
|
24684
|
-
if (value) res.raw.write(Buffer.from(value));
|
|
24685
|
-
}
|
|
24686
|
-
} finally {
|
|
24687
|
-
clearTimeout(timer);
|
|
24688
|
-
res.raw.end();
|
|
24689
|
-
}
|
|
25075
|
+
await streamEventStream(res, upstream, timer);
|
|
24690
25076
|
return;
|
|
24691
25077
|
}
|
|
24692
25078
|
clearTimeout(timer);
|
|
@@ -24803,13 +25189,13 @@ function handleGalleryDeploy(router) {
|
|
|
24803
25189
|
res.json({ deployed: name, files: copied });
|
|
24804
25190
|
};
|
|
24805
25191
|
}
|
|
24806
|
-
function
|
|
25192
|
+
function realPathOf2(target) {
|
|
24807
25193
|
let existing = target;
|
|
24808
25194
|
const tail = [];
|
|
24809
25195
|
for (; ; ) {
|
|
24810
25196
|
let present = false;
|
|
24811
25197
|
try {
|
|
24812
|
-
|
|
25198
|
+
lstatSync3(existing);
|
|
24813
25199
|
present = true;
|
|
24814
25200
|
} catch {
|
|
24815
25201
|
}
|
|
@@ -24821,9 +25207,9 @@ function realPathOf(target) {
|
|
|
24821
25207
|
}
|
|
24822
25208
|
function safeJoin(projectRoot3, rel, extraRoots = []) {
|
|
24823
25209
|
try {
|
|
24824
|
-
const root =
|
|
24825
|
-
const resolved =
|
|
24826
|
-
const roots = [root, ...extraRoots.filter(Boolean).map((r) =>
|
|
25210
|
+
const root = realPathOf2(resolve12(projectRoot3));
|
|
25211
|
+
const resolved = realPathOf2(resolve12(root, rel));
|
|
25212
|
+
const roots = [root, ...extraRoots.filter(Boolean).map((r) => realPathOf2(resolve12(r)))];
|
|
24827
25213
|
for (const r of roots) {
|
|
24828
25214
|
if (resolved === r || resolved.startsWith(r.endsWith(sep6) ? r : r + sep6)) return resolved;
|
|
24829
25215
|
}
|
|
@@ -24832,7 +25218,7 @@ function safeJoin(projectRoot3, rel, extraRoots = []) {
|
|
|
24832
25218
|
return null;
|
|
24833
25219
|
}
|
|
24834
25220
|
function resolvedRelative(projectRoot3, resolvedPath) {
|
|
24835
|
-
return relative9(
|
|
25221
|
+
return relative9(realPathOf2(resolve12(projectRoot3)), resolvedPath);
|
|
24836
25222
|
}
|
|
24837
25223
|
function devFilesHidden(name) {
|
|
24838
25224
|
if (DEV_FILES_IGNORED.has(name)) return true;
|
|
@@ -24860,7 +25246,7 @@ async function devGitInfo(root) {
|
|
|
24860
25246
|
if (!inside || inside.trim() !== "true") return empty;
|
|
24861
25247
|
const branch = (git(["rev-parse", "--abbrev-ref", "HEAD"]) ?? "").trim();
|
|
24862
25248
|
const topRaw = git(["rev-parse", "--show-toplevel"]);
|
|
24863
|
-
const gitRoot = topRaw && topRaw.trim() !== "" ? topRaw.trim().replace(/\\/g, "/")
|
|
25249
|
+
const gitRoot = topRaw && topRaw.trim() !== "" ? rtrimSlashes(topRaw.trim().replace(/\\/g, "/")) : null;
|
|
24864
25250
|
const status2 = /* @__PURE__ */ new Map();
|
|
24865
25251
|
const porcelain = git(["status", "--porcelain", "-uall"]);
|
|
24866
25252
|
if (porcelain) {
|
|
@@ -24879,6 +25265,44 @@ async function devGitInfo(root) {
|
|
|
24879
25265
|
return empty;
|
|
24880
25266
|
}
|
|
24881
25267
|
}
|
|
25268
|
+
function devGitLabel(gitPath, isDir, gitStatus) {
|
|
25269
|
+
const code = gitStatus.get(gitPath);
|
|
25270
|
+
if (code !== void 0) return devGitStatusLabel(code);
|
|
25271
|
+
if (isDir) {
|
|
25272
|
+
const prefix = gitPath + "/";
|
|
25273
|
+
for (const [gf, gc] of gitStatus) {
|
|
25274
|
+
if (gf.startsWith(prefix)) return gc === "??" ? "untracked" : "modified";
|
|
25275
|
+
}
|
|
25276
|
+
}
|
|
25277
|
+
return "clean";
|
|
25278
|
+
}
|
|
25279
|
+
function devDirHasVisibleChild(full) {
|
|
25280
|
+
try {
|
|
25281
|
+
for (const c of readdirSync13(full)) {
|
|
25282
|
+
if (!devFilesHidden(c)) return true;
|
|
25283
|
+
}
|
|
25284
|
+
} catch {
|
|
25285
|
+
}
|
|
25286
|
+
return false;
|
|
25287
|
+
}
|
|
25288
|
+
function buildFileEntry(name, full, entryRel, cwdInGit, gitStatus) {
|
|
25289
|
+
let isDir = false;
|
|
25290
|
+
let size = null;
|
|
25291
|
+
try {
|
|
25292
|
+
const st = statSync15(full);
|
|
25293
|
+
isDir = st.isDirectory();
|
|
25294
|
+
if (!isDir) size = st.size;
|
|
25295
|
+
} catch {
|
|
25296
|
+
}
|
|
25297
|
+
return {
|
|
25298
|
+
name,
|
|
25299
|
+
path: entryRel,
|
|
25300
|
+
is_dir: isDir,
|
|
25301
|
+
has_children: isDir ? devDirHasVisibleChild(full) : null,
|
|
25302
|
+
git_status: devGitLabel(cwdInGit + entryRel, isDir, gitStatus),
|
|
25303
|
+
size
|
|
25304
|
+
};
|
|
25305
|
+
}
|
|
24882
25306
|
function devAdminLanguage(rel) {
|
|
24883
25307
|
const base = (rel.split(/[\\/]/).pop() ?? "").toLowerCase();
|
|
24884
25308
|
if (base === "dockerfile" || base === "dockerfile.dev" || base === "dockerfile.prod") {
|
|
@@ -24893,14 +25317,14 @@ function devAdminLanguage(rel) {
|
|
|
24893
25317
|
function readDevFile(target) {
|
|
24894
25318
|
let fd;
|
|
24895
25319
|
try {
|
|
24896
|
-
fd =
|
|
24897
|
-
if (!
|
|
25320
|
+
fd = openSync5(target, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
|
|
25321
|
+
if (!fstatSync3(fd).isFile()) return null;
|
|
24898
25322
|
return readFileSync18(fd);
|
|
24899
25323
|
} catch (error2) {
|
|
24900
25324
|
if (["ENOENT", "ENOTDIR", "ELOOP"].includes(error2.code ?? "")) return null;
|
|
24901
25325
|
throw error2;
|
|
24902
25326
|
} finally {
|
|
24903
|
-
if (fd !== void 0)
|
|
25327
|
+
if (fd !== void 0) closeSync5(fd);
|
|
24904
25328
|
}
|
|
24905
25329
|
}
|
|
24906
25330
|
function mcpSecureEqual(expected, provided) {
|
|
@@ -25127,10 +25551,11 @@ function toolbarJs() {
|
|
|
25127
25551
|
connect();
|
|
25128
25552
|
})();`;
|
|
25129
25553
|
}
|
|
25130
|
-
var cpuCount, DEV_MCP_PREFIXES, DEV_PUBLIC_ASSETS, DEV_SECRET_BASENAMES, DEV_SECRET_SUFFIXES, MessageLog, RequestInspector, ErrorTracker, DevMailboxStore, DevQueue, WsTracker, DevAdmin, handleDashboard, _reloadMtime, _reloadFile, handleMtime, handleReload, handleMessages, handleMessagesClear, handleRequests, handleRequestsClear, handleSystem, handleMessagesSearch, handleQueue, handleQueueTopics, handleQueueDeadLetters, handleQueueRetry, handleQueuePurge, handleQueueReplay, handleMailbox, handleMailboxRead, handleMailboxSeed, handleMailboxClear, handleTable, handleTables, handleSeed, handleQuery, handleBroken, handleBrokenResolve, handleBrokenClear, handleWebsockets, handleWebsocketsDisconnect, handleTool, DEFAULT_MCP_URL, handleGroundingStatus, handleGroundingToken, handleMigrate, handleSeedRun, handleTest, handleThreads, handleThreadsSub, handleConnections, handleConnectionsTest, handleConnectionsSave, __devAdminFilename, __devAdminDirname, handleGalleryList, handleVersionCheck, handleThoughts, handleSuperviseStub, DEV_FILES_IGNORED, handleFiles, DEV_ADMIN_LANG_MAP, handleFileRead, handleFileSave, handleFileRaw, handleFileRename, handleFileDelete, handleDepsSearch, handleDepsInstall, handleGitStatus, handleMcpTools, handleMcpCall, handleMcpStreamable, handleMcpDelete, handleMcpGet405, handleMcpLegacyMessage, handleMcpSse, handleScaffoldList, handleScaffoldRun, handlePlanCurrent, handlePlanList, handlePlanCreate, handlePlanSwitch, handlePlanCompleteStep, handlePlanAddStep, handlePlanNote, handlePlanArchive, handlePlanRead, handlePlanFlesh, handleIndexRebuild, handleIndexSearch, handleIndexFile, handleIndexOverview, handleDocsSearch, handleDocsClass, handleDocsMethod, handleDocsIndex, handleDocsWellKnown, handleDevAdminJs, handleToolbarCss, handleToolbarJs;
|
|
25554
|
+
var cpuCount, DEV_MCP_PREFIXES, DEV_PUBLIC_ASSETS, DEV_SECRET_BASENAMES, DEV_SECRET_SUFFIXES, MessageLog, RequestInspector, ErrorTracker, DevMailboxStore, DevQueue, WsTracker, DevAdmin, handleDashboard, _reloadMtime, _reloadFile, handleMtime, handleReload, handleMessages, handleMessagesClear, handleRequests, handleRequestsClear, handleSystem, handleMessagesSearch, handleQueue, handleQueueTopics, handleQueueDeadLetters, handleQueueRetry, handleQueuePurge, handleQueueReplay, handleMailbox, handleMailboxRead, handleMailboxSeed, handleMailboxClear, handleTable, handleTables, handleSeed, handleQuery, handleBroken, handleBrokenResolve, handleBrokenClear, handleWebsockets, handleWebsocketsDisconnect, handleTool, DEFAULT_MCP_URL, handleGroundingStatus, handleGroundingToken, handleMigrate, handleSeedRun, handleTest, handleThreads, handleThreadsSub, handleConnections, handleConnectionsTest, handleConnectionsSave, __devAdminFilename, __devAdminDirname, handleGalleryList, handleVersionCheck, handleThoughts, handleSuperviseStub, DEV_FILES_IGNORED, handleFiles, DEV_ADMIN_LANG_MAP, handleFileRead, handleFileSave, handleFileRaw, handleFileRename, handleFileDelete, handleDepsSearch, handleDepsInstall, handleGitStatus, handleMcpTools, handleMcpCall, handleMcpStreamable, handleMcpDelete, handleMcpGet405, handleMcpLegacyMessage, handleMcpSse, handleScaffoldList, handleScaffoldRun, handlePlanCurrent, handlePlanList, handlePlanCreate, handlePlanSwitch, handlePlanCompleteStep, handlePlanAddStep, handlePlanNote, handlePlanArchive, handlePlanRead, handlePlanFlesh, handleIndexRebuild, handleIndexSearch, handleIndexFile, handleIndexOverview, handleDocsSearch, handleDocsClass, handleDocsMethod, handleDocsIndex, handleDocsWellKnown, handleDevAdminJs, handleDevAdminSplit, handleToolbarCss, handleToolbarJs;
|
|
25131
25555
|
var init_devAdmin = __esm({
|
|
25132
25556
|
"../core/src/devAdmin.ts"() {
|
|
25133
25557
|
"use strict";
|
|
25558
|
+
init_slashTrim();
|
|
25134
25559
|
init_devMailbox();
|
|
25135
25560
|
init_upstreamResponse();
|
|
25136
25561
|
init_dotenv();
|
|
@@ -25597,6 +26022,7 @@ var init_devAdmin = __esm({
|
|
|
25597
26022
|
{ method: "GET", pattern: "/__dev/api/docs/.well-known.json", handler: handleDocsWellKnown },
|
|
25598
26023
|
// JS asset
|
|
25599
26024
|
{ method: "GET", pattern: "/__dev/js/tina4-dev-admin.min.js", handler: handleDevAdminJs },
|
|
26025
|
+
{ method: "GET", pattern: "/__dev/split", handler: handleDevAdminSplit },
|
|
25600
26026
|
// Dev-toolbar assets — served as external CSS/JS so the injected toolbar is
|
|
25601
26027
|
// CSP-clean (no inline style=, onclick=, or <script>) under a strict
|
|
25602
26028
|
// `default-src 'self'`. Parity with PHP's /__dev/toolbar.css + toolbar.js.
|
|
@@ -25963,13 +26389,7 @@ var init_devAdmin = __esm({
|
|
|
25963
26389
|
};
|
|
25964
26390
|
handleSeed = async (req2, res) => {
|
|
25965
26391
|
const url = new URL(req2.url ?? "/", "http://localhost");
|
|
25966
|
-
const
|
|
25967
|
-
const table2 = url.searchParams.get("table") ?? body?.table ?? "";
|
|
25968
|
-
const count = parseInt(String(url.searchParams.get("count") ?? body?.count ?? "10"), 10) || 10;
|
|
25969
|
-
const seedRaw = url.searchParams.get("seed") ?? body?.seed;
|
|
25970
|
-
const seed = seedRaw !== void 0 && seedRaw !== null && String(seedRaw) !== "" ? Number.isNaN(parseInt(String(seedRaw), 10)) ? void 0 : parseInt(String(seedRaw), 10) : void 0;
|
|
25971
|
-
const clear = String(url.searchParams.get("clear") ?? body?.clear ?? "") === "true" || body?.clear === true;
|
|
25972
|
-
const strict = String(url.searchParams.get("strict") ?? body?.strict ?? "") === "true" || body?.strict === true;
|
|
26392
|
+
const { table: table2, count, seed, clear, strict } = parseSeedRequest(url, req2.body);
|
|
25973
26393
|
if (!table2) {
|
|
25974
26394
|
res.json({ error: "Missing table parameter" });
|
|
25975
26395
|
return;
|
|
@@ -25984,33 +26404,7 @@ var init_devAdmin = __esm({
|
|
|
25984
26404
|
res.json({ error: `Table '${table2}' not found or has no columns` });
|
|
25985
26405
|
return;
|
|
25986
26406
|
}
|
|
25987
|
-
const fieldMap =
|
|
25988
|
-
for (const col of columns) {
|
|
25989
|
-
const name = col.name.toLowerCase();
|
|
25990
|
-
const type2 = col.type.toLowerCase();
|
|
25991
|
-
if (name === "id" || col.primaryKey === true) continue;
|
|
25992
|
-
if (name.includes("email")) {
|
|
25993
|
-
fieldMap[col.name] = () => fake.email();
|
|
25994
|
-
} else if (name.includes("name")) {
|
|
25995
|
-
fieldMap[col.name] = () => fake.name();
|
|
25996
|
-
} else if (name.includes("phone")) {
|
|
25997
|
-
fieldMap[col.name] = () => fake.phone();
|
|
25998
|
-
} else if (name.includes("address") || name.includes("city") || name.includes("country")) {
|
|
25999
|
-
fieldMap[col.name] = () => fake.address();
|
|
26000
|
-
} else if (name.includes("url") || name.includes("website")) {
|
|
26001
|
-
fieldMap[col.name] = () => fake.url();
|
|
26002
|
-
} else if (type2.includes("int")) {
|
|
26003
|
-
fieldMap[col.name] = () => fake.integer(1, 1e3);
|
|
26004
|
-
} else if (type2.includes("real") || type2.includes("float") || type2.includes("double") || type2.includes("numeric") || type2.includes("decimal")) {
|
|
26005
|
-
fieldMap[col.name] = () => fake.numeric(0, 1e3, 2);
|
|
26006
|
-
} else if (type2.includes("bool")) {
|
|
26007
|
-
fieldMap[col.name] = () => fake.boolean();
|
|
26008
|
-
} else if (type2.includes("date") || type2.includes("time")) {
|
|
26009
|
-
fieldMap[col.name] = () => fake.date();
|
|
26010
|
-
} else {
|
|
26011
|
-
fieldMap[col.name] = () => fake.sentence(3);
|
|
26012
|
-
}
|
|
26013
|
-
}
|
|
26407
|
+
const fieldMap = buildSeedFieldMap(columns, fake);
|
|
26014
26408
|
const summary = await seedTable2(db, table2, count, fieldMap, void 0, { clear, strict });
|
|
26015
26409
|
res.json({ seeded: summary.seeded, failed: summary.failed, errors: summary.errors, table: table2 });
|
|
26016
26410
|
} catch (e) {
|
|
@@ -26183,9 +26577,9 @@ var init_devAdmin = __esm({
|
|
|
26183
26577
|
};
|
|
26184
26578
|
handleTest = async (_req, res) => {
|
|
26185
26579
|
try {
|
|
26186
|
-
const { execFile } = await import("node:child_process");
|
|
26580
|
+
const { execFile: execFile2 } = await import("node:child_process");
|
|
26187
26581
|
const { promisify } = await import("node:util");
|
|
26188
|
-
const run = promisify(
|
|
26582
|
+
const run = promisify(execFile2);
|
|
26189
26583
|
try {
|
|
26190
26584
|
const { stdout, stderr } = await run("npm", ["test"], {
|
|
26191
26585
|
cwd: resolve12(process.cwd()),
|
|
@@ -26303,9 +26697,9 @@ var init_devAdmin = __esm({
|
|
|
26303
26697
|
}
|
|
26304
26698
|
try {
|
|
26305
26699
|
const envPath = join22(process.cwd(), ".env");
|
|
26306
|
-
const fd =
|
|
26700
|
+
const fd = openSync5(envPath, constants4.O_RDWR | constants4.O_APPEND | constants4.O_CREAT | (constants4.O_NOFOLLOW ?? 0) | (constants4.O_NONBLOCK ?? 0), 384);
|
|
26307
26701
|
try {
|
|
26308
|
-
const info =
|
|
26702
|
+
const info = fstatSync3(fd);
|
|
26309
26703
|
if (!info.isFile() || info.nlink !== 1) throw new Error("Refusing a non-regular or multiply-linked configuration file");
|
|
26310
26704
|
fchmodSync2(fd, 384);
|
|
26311
26705
|
const lines = readFileSync18(fd, "utf-8").split("\n");
|
|
@@ -26335,10 +26729,10 @@ var init_devAdmin = __esm({
|
|
|
26335
26729
|
for (const [key, found] of Object.entries(keysFound)) {
|
|
26336
26730
|
if (!found) newLines.push(`${key}=${values[key]}`);
|
|
26337
26731
|
}
|
|
26338
|
-
|
|
26732
|
+
ftruncateSync2(fd, 0);
|
|
26339
26733
|
writeFileSync13(fd, newLines.join("\n") + "\n");
|
|
26340
26734
|
} finally {
|
|
26341
|
-
|
|
26735
|
+
closeSync5(fd);
|
|
26342
26736
|
}
|
|
26343
26737
|
res.json({ success: true });
|
|
26344
26738
|
} catch (e) {
|
|
@@ -26461,48 +26855,7 @@ var init_devAdmin = __esm({
|
|
|
26461
26855
|
const full = join22(target, name);
|
|
26462
26856
|
const entryRel = relative9(root, full).replace(/\\/g, "/");
|
|
26463
26857
|
if (isSecretPath(entryRel)) continue;
|
|
26464
|
-
|
|
26465
|
-
let size = null;
|
|
26466
|
-
try {
|
|
26467
|
-
const st = statSync15(full);
|
|
26468
|
-
isDir = st.isDirectory();
|
|
26469
|
-
if (!isDir) size = st.size;
|
|
26470
|
-
} catch {
|
|
26471
|
-
}
|
|
26472
|
-
const gitPath = cwdInGit + entryRel;
|
|
26473
|
-
let gitLabel = "clean";
|
|
26474
|
-
const code = gitStatus.get(gitPath);
|
|
26475
|
-
if (code !== void 0) {
|
|
26476
|
-
gitLabel = devGitStatusLabel(code);
|
|
26477
|
-
} else if (isDir) {
|
|
26478
|
-
const prefix = gitPath + "/";
|
|
26479
|
-
for (const [gf, gc] of gitStatus) {
|
|
26480
|
-
if (gf.startsWith(prefix)) {
|
|
26481
|
-
gitLabel = gc === "??" ? "untracked" : "modified";
|
|
26482
|
-
break;
|
|
26483
|
-
}
|
|
26484
|
-
}
|
|
26485
|
-
}
|
|
26486
|
-
let hasChildren = null;
|
|
26487
|
-
if (isDir) {
|
|
26488
|
-
hasChildren = false;
|
|
26489
|
-
try {
|
|
26490
|
-
for (const c of readdirSync13(full)) {
|
|
26491
|
-
if (devFilesHidden(c)) continue;
|
|
26492
|
-
hasChildren = true;
|
|
26493
|
-
break;
|
|
26494
|
-
}
|
|
26495
|
-
} catch {
|
|
26496
|
-
}
|
|
26497
|
-
}
|
|
26498
|
-
entries.push({
|
|
26499
|
-
name,
|
|
26500
|
-
path: entryRel,
|
|
26501
|
-
is_dir: isDir,
|
|
26502
|
-
has_children: hasChildren,
|
|
26503
|
-
git_status: gitLabel,
|
|
26504
|
-
size
|
|
26505
|
-
});
|
|
26858
|
+
entries.push(buildFileEntry(name, full, entryRel, cwdInGit, gitStatus));
|
|
26506
26859
|
}
|
|
26507
26860
|
res.json({ path: relative9(root, target).replace(/\\/g, "/") || ".", branch, entries });
|
|
26508
26861
|
};
|
|
@@ -27035,6 +27388,31 @@ var init_devAdmin = __esm({
|
|
|
27035
27388
|
res.raw.writeHead(404, { "Content-Type": "text/plain" });
|
|
27036
27389
|
res.raw.end("tina4-dev-admin.min.js not found");
|
|
27037
27390
|
};
|
|
27391
|
+
handleDevAdminSplit = async (_req, res) => {
|
|
27392
|
+
const { readFileSync: readFileSync27, existsSync: existsSync29 } = await import("node:fs");
|
|
27393
|
+
const { dirname: dirname17, join: join33, resolve: resolve21 } = await import("node:path");
|
|
27394
|
+
const { fileURLToPath: fileURLToPath10 } = await import("node:url");
|
|
27395
|
+
const dir = dirname17(fileURLToPath10(import.meta.url));
|
|
27396
|
+
const candidates = [
|
|
27397
|
+
join33(dir, "..", "public", "js", "tina4-dev-admin-split.html"),
|
|
27398
|
+
join33(dir, "..", "..", "public", "js", "tina4-dev-admin-split.html"),
|
|
27399
|
+
resolve21(process.cwd(), "node_modules", "tina4-nodejs", "packages", "core", "public", "js", "tina4-dev-admin-split.html"),
|
|
27400
|
+
resolve21(process.cwd(), "public", "js", "tina4-dev-admin-split.html")
|
|
27401
|
+
];
|
|
27402
|
+
for (const path10 of candidates) {
|
|
27403
|
+
if (existsSync29(path10)) {
|
|
27404
|
+
try {
|
|
27405
|
+
const content = readFileSync27(path10, "utf-8");
|
|
27406
|
+
res.raw.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-cache" });
|
|
27407
|
+
res.raw.end(content);
|
|
27408
|
+
return;
|
|
27409
|
+
} catch {
|
|
27410
|
+
}
|
|
27411
|
+
}
|
|
27412
|
+
}
|
|
27413
|
+
res.raw.writeHead(404, { "Content-Type": "text/plain" });
|
|
27414
|
+
res.raw.end("tina4-dev-admin-split.html not found");
|
|
27415
|
+
};
|
|
27038
27416
|
handleToolbarCss = (_req, res) => {
|
|
27039
27417
|
res.raw.writeHead(200, { "Content-Type": "text/css; charset=utf-8", "Cache-Control": "no-cache" });
|
|
27040
27418
|
res.raw.end(toolbarCss());
|
|
@@ -27506,7 +27884,10 @@ function generate2(routes, models = []) {
|
|
|
27506
27884
|
openapi: resolveOpenApiVersion(),
|
|
27507
27885
|
info: buildInfo(),
|
|
27508
27886
|
servers: resolveServers(),
|
|
27509
|
-
|
|
27887
|
+
// Prototype-less: route paths are developer-defined, but a null prototype
|
|
27888
|
+
// makes `spec.paths[path][method] = op` structurally pollution-proof
|
|
27889
|
+
// regardless (js/prototype-polluting-assignment). Serialises to JSON the same.
|
|
27890
|
+
paths: /* @__PURE__ */ Object.create(null),
|
|
27510
27891
|
components: {
|
|
27511
27892
|
schemas: {},
|
|
27512
27893
|
// Configurable security schemes (v3.13.42): bearerFormat via env, optional
|
|
@@ -27571,7 +27952,7 @@ function buildOperation(route, spec, ctx) {
|
|
|
27571
27952
|
const openApiPath = patternToOpenAPI(route.pattern);
|
|
27572
27953
|
const method = route.method.toLowerCase();
|
|
27573
27954
|
if (!spec.paths[openApiPath]) {
|
|
27574
|
-
spec.paths[openApiPath] =
|
|
27955
|
+
spec.paths[openApiPath] = /* @__PURE__ */ Object.create(null);
|
|
27575
27956
|
}
|
|
27576
27957
|
const tags = route.meta?.tags ?? inferTags(route.pattern);
|
|
27577
27958
|
for (const t of tags) {
|
|
@@ -27877,7 +28258,7 @@ function inferModelFromPath(pattern) {
|
|
|
27877
28258
|
return null;
|
|
27878
28259
|
}
|
|
27879
28260
|
function operationIdBase(method, openApiPath) {
|
|
27880
|
-
const clean = openApiPath
|
|
28261
|
+
const clean = trimSlashes(openApiPath).replace(/\//g, "_").replace(/\.\.\./g, "").replace(/[{}]/g, "").replace(/\*/g, "wildcard");
|
|
27881
28262
|
return clean ? `${method}_${clean}` : method;
|
|
27882
28263
|
}
|
|
27883
28264
|
function uniqueOperationId(method, openApiPath, seen) {
|
|
@@ -27895,6 +28276,7 @@ var WRITE_METHODS, INTERNAL_PREFIXES, registeredSchemes, registeredSchemas, PARA
|
|
|
27895
28276
|
var init_generator = __esm({
|
|
27896
28277
|
"../swagger/src/generator.ts"() {
|
|
27897
28278
|
"use strict";
|
|
28279
|
+
init_slashTrim();
|
|
27898
28280
|
WRITE_METHODS = /* @__PURE__ */ new Set(["post", "put", "patch", "delete"]);
|
|
27899
28281
|
INTERNAL_PREFIXES = ["/swagger", "/__dev", "/__feedback", "/ai", "/rag", "/vision", "/embed", "/image"];
|
|
27900
28282
|
registeredSchemes = {};
|
|
@@ -27916,7 +28298,7 @@ var init_generator = __esm({
|
|
|
27916
28298
|
|
|
27917
28299
|
// ../swagger/src/ui.ts
|
|
27918
28300
|
function swaggerUiCdn() {
|
|
27919
|
-
return (process.env.TINA4_SWAGGER_UI_CDN ?? "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5")
|
|
28301
|
+
return rtrimSlashes(process.env.TINA4_SWAGGER_UI_CDN ?? "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5");
|
|
27920
28302
|
}
|
|
27921
28303
|
function swaggerEnabled() {
|
|
27922
28304
|
const raw = (process.env.TINA4_SWAGGER_ENABLED ?? "").trim().toLowerCase();
|
|
@@ -27972,6 +28354,7 @@ var SWAGGER_UI_HTML;
|
|
|
27972
28354
|
var init_ui = __esm({
|
|
27973
28355
|
"../swagger/src/ui.ts"() {
|
|
27974
28356
|
"use strict";
|
|
28357
|
+
init_slashTrim();
|
|
27975
28358
|
SWAGGER_UI_HTML = (specUrl, nonce) => `<!DOCTYPE html>
|
|
27976
28359
|
<html lang="en">
|
|
27977
28360
|
<head>
|
|
@@ -28389,7 +28772,7 @@ import { resolve as resolve15, dirname as dirname12, join as join25, relative as
|
|
|
28389
28772
|
import { existsSync as existsSync23, readdirSync as readdirSync15, readFileSync as readFileSync21, statSync as statSync16 } from "node:fs";
|
|
28390
28773
|
import { isatty } from "node:tty";
|
|
28391
28774
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
28392
|
-
import { execFileSync as execFileSync3,
|
|
28775
|
+
import { execFileSync as execFileSync3, execFile } from "node:child_process";
|
|
28393
28776
|
import cluster from "node:cluster";
|
|
28394
28777
|
import os2 from "node:os";
|
|
28395
28778
|
function isSwaggerAssetPath(pathname) {
|
|
@@ -28525,10 +28908,11 @@ function shouldOpenBrowser(isDevelopment, noBrowser = false) {
|
|
|
28525
28908
|
});
|
|
28526
28909
|
}
|
|
28527
28910
|
function openBrowser(url) {
|
|
28911
|
+
if (!/^https?:\/\/([A-Za-z0-9.-]+|\[[0-9A-Fa-f:]+\])(:\d{1,5})?\/?$/.test(url)) return;
|
|
28528
28912
|
setTimeout(() => {
|
|
28529
|
-
if (process.platform === "darwin")
|
|
28530
|
-
else if (process.platform === "win32")
|
|
28531
|
-
else
|
|
28913
|
+
if (process.platform === "darwin") execFile("open", [url]);
|
|
28914
|
+
else if (process.platform === "win32") execFile("rundll32", ["url.dll,FileProtocolHandler", url]);
|
|
28915
|
+
else execFile("xdg-open", [url]);
|
|
28532
28916
|
}, 2e3);
|
|
28533
28917
|
}
|
|
28534
28918
|
function resolvePortAndHost(config) {
|
|
@@ -28643,7 +29027,7 @@ function resetTemplateCache() {
|
|
|
28643
29027
|
}
|
|
28644
29028
|
function resolveTemplate(pathname, templatesDir) {
|
|
28645
29029
|
if (!templateAutoRoutingEnabled()) return null;
|
|
28646
|
-
const cleanPath = pathname
|
|
29030
|
+
const cleanPath = trimSlashes(pathname) || "index";
|
|
28647
29031
|
const isDev2 = (process.env.TINA4_DEBUG ?? "false").toLowerCase() === "true";
|
|
28648
29032
|
if (isDev2) {
|
|
28649
29033
|
if (cleanPath.split("/").some((seg) => seg.startsWith("_"))) return null;
|
|
@@ -28911,7 +29295,7 @@ async function runGlobalMiddlewarePass(middleware, req2, res) {
|
|
|
28911
29295
|
}
|
|
28912
29296
|
function resolveHandlerArgs(handler, req2, res, routeParams) {
|
|
28913
29297
|
const fnStr = handler.toString();
|
|
28914
|
-
const argMatch = fnStr.match(/^(?:async\s*)?(?:function
|
|
29298
|
+
const argMatch = fnStr.match(/^(?:async\s*)?(?:function(?:\s+\w+)?\s*)?\(([^)]*)\)/);
|
|
28915
29299
|
const argNames = argMatch?.[1]?.split(",").map((a) => a.trim().replace(/[:=].*/, "")) ?? [];
|
|
28916
29300
|
const filteredArgs = argNames.filter((n) => n.length > 0);
|
|
28917
29301
|
if (filteredArgs.length === 0) return [];
|
|
@@ -29147,8 +29531,9 @@ async function buildDispatchContext(router, base) {
|
|
|
29147
29531
|
const templatesDir = resolve15(root, "src/templates");
|
|
29148
29532
|
let frondEngine = null;
|
|
29149
29533
|
try {
|
|
29150
|
-
const { Frond: Frond2, setCspNonceProvider: setCspNonceProvider2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
29534
|
+
const { Frond: Frond2, setCspNonceProvider: setCspNonceProvider2, setFormTokenSessionProvider: setFormTokenSessionProvider2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
29151
29535
|
setCspNonceProvider2(currentCspNonce);
|
|
29536
|
+
setFormTokenSessionProvider2(currentFormTokenSessionId);
|
|
29152
29537
|
frondEngine = new Frond2(templatesDir);
|
|
29153
29538
|
} catch {
|
|
29154
29539
|
}
|
|
@@ -29263,7 +29648,10 @@ async function runDispatch(ctx, rawReq, rawRes) {
|
|
|
29263
29648
|
const requestNonce = generateNonce();
|
|
29264
29649
|
return Log.runWithRequestId(
|
|
29265
29650
|
requestId,
|
|
29266
|
-
() => runWithCspNonce(
|
|
29651
|
+
() => runWithCspNonce(
|
|
29652
|
+
requestNonce,
|
|
29653
|
+
() => runWithFormTokenSession(() => dispatchInner(ctx, rawReq, rawRes, requestId))
|
|
29654
|
+
)
|
|
29267
29655
|
);
|
|
29268
29656
|
}
|
|
29269
29657
|
function loopbackBindHosts(host) {
|
|
@@ -29391,8 +29779,9 @@ async function startServer(config) {
|
|
|
29391
29779
|
let frondEngine = null;
|
|
29392
29780
|
setDefaultTemplatesDir(templatesDir);
|
|
29393
29781
|
try {
|
|
29394
|
-
const { Frond: Frond2, setCspNonceProvider: setCspNonceProvider2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
29782
|
+
const { Frond: Frond2, setCspNonceProvider: setCspNonceProvider2, setFormTokenSessionProvider: setFormTokenSessionProvider2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
29395
29783
|
setCspNonceProvider2(currentCspNonce);
|
|
29784
|
+
setFormTokenSessionProvider2(currentFormTokenSessionId);
|
|
29396
29785
|
frondEngine = new Frond2(templatesDir);
|
|
29397
29786
|
router.addRoute({
|
|
29398
29787
|
method: "GET",
|
|
@@ -29741,6 +30130,7 @@ var __filename, __dirname, BUILTIN_ERROR_TEMPLATES_DIR, BUILTIN_PUBLIC_DIR, swag
|
|
|
29741
30130
|
var init_server = __esm({
|
|
29742
30131
|
"../core/src/server.ts"() {
|
|
29743
30132
|
"use strict";
|
|
30133
|
+
init_slashTrim();
|
|
29744
30134
|
init_transport();
|
|
29745
30135
|
init_router();
|
|
29746
30136
|
init_authGate();
|
|
@@ -29748,6 +30138,7 @@ var init_server = __esm({
|
|
|
29748
30138
|
init_portTakeover();
|
|
29749
30139
|
init_request();
|
|
29750
30140
|
init_dispatchPipeline();
|
|
30141
|
+
init_session();
|
|
29751
30142
|
init_response();
|
|
29752
30143
|
init_middleware();
|
|
29753
30144
|
init_static();
|
|
@@ -31894,6 +32285,7 @@ var ApiStreamError, RETRY_STATUSES, DOWNLOAD_CHUNK_SIZE, MAX_REDIRECTS, STRIP_ON
|
|
|
31894
32285
|
var init_api = __esm({
|
|
31895
32286
|
"../core/src/api.ts"() {
|
|
31896
32287
|
"use strict";
|
|
32288
|
+
init_slashTrim();
|
|
31897
32289
|
init_version();
|
|
31898
32290
|
init_ssrf();
|
|
31899
32291
|
ApiStreamError = class extends Error {
|
|
@@ -31991,7 +32383,7 @@ var init_api = __esm({
|
|
|
31991
32383
|
* turns on a per-client, in-memory cookie jar.
|
|
31992
32384
|
*/
|
|
31993
32385
|
constructor(baseUrl = "", authHeaderOrOptions = "", timeout = 30) {
|
|
31994
|
-
this.baseUrl = baseUrl
|
|
32386
|
+
this.baseUrl = rtrimSlashes(baseUrl);
|
|
31995
32387
|
this.headers = {};
|
|
31996
32388
|
this.maxRetries = 0;
|
|
31997
32389
|
this.retryBackoff = 0.5;
|
|
@@ -32757,6 +33149,23 @@ function sendCommand(socket, command) {
|
|
|
32757
33149
|
});
|
|
32758
33150
|
});
|
|
32759
33151
|
}
|
|
33152
|
+
function pushAlternativeBody(lines, altBoundary, text, body) {
|
|
33153
|
+
lines.push(`Content-Type: multipart/alternative; boundary="${altBoundary}"`);
|
|
33154
|
+
lines.push("");
|
|
33155
|
+
lines.push(`--${altBoundary}`);
|
|
33156
|
+
lines.push("Content-Type: text/plain; charset=UTF-8");
|
|
33157
|
+
lines.push("Content-Transfer-Encoding: 7bit");
|
|
33158
|
+
lines.push("");
|
|
33159
|
+
lines.push(text);
|
|
33160
|
+
lines.push("");
|
|
33161
|
+
lines.push(`--${altBoundary}`);
|
|
33162
|
+
lines.push("Content-Type: text/html; charset=UTF-8");
|
|
33163
|
+
lines.push("Content-Transfer-Encoding: 7bit");
|
|
33164
|
+
lines.push("");
|
|
33165
|
+
lines.push(body);
|
|
33166
|
+
lines.push("");
|
|
33167
|
+
lines.push(`--${altBoundary}--`);
|
|
33168
|
+
}
|
|
32760
33169
|
function buildMimeMessage(options) {
|
|
32761
33170
|
const boundary = `----=_Tina4_${Date.now()}_${Math.random().toString(36).substring(2)}`;
|
|
32762
33171
|
const altBoundary = `----=_Tina4Alt_${Date.now()}_${Math.random().toString(36).substring(2)}`;
|
|
@@ -32786,21 +33195,7 @@ function buildMimeMessage(options) {
|
|
|
32786
33195
|
lines.push("");
|
|
32787
33196
|
lines.push(`--${boundary}`);
|
|
32788
33197
|
if (hasTextAlt) {
|
|
32789
|
-
lines.
|
|
32790
|
-
lines.push("");
|
|
32791
|
-
lines.push(`--${altBoundary}`);
|
|
32792
|
-
lines.push("Content-Type: text/plain; charset=UTF-8");
|
|
32793
|
-
lines.push("Content-Transfer-Encoding: 7bit");
|
|
32794
|
-
lines.push("");
|
|
32795
|
-
lines.push(options.text);
|
|
32796
|
-
lines.push("");
|
|
32797
|
-
lines.push(`--${altBoundary}`);
|
|
32798
|
-
lines.push("Content-Type: text/html; charset=UTF-8");
|
|
32799
|
-
lines.push("Content-Transfer-Encoding: 7bit");
|
|
32800
|
-
lines.push("");
|
|
32801
|
-
lines.push(options.body);
|
|
32802
|
-
lines.push("");
|
|
32803
|
-
lines.push(`--${altBoundary}--`);
|
|
33198
|
+
pushAlternativeBody(lines, altBoundary, options.text, options.body);
|
|
32804
33199
|
} else {
|
|
32805
33200
|
const contentType = options.html ? "text/html" : "text/plain";
|
|
32806
33201
|
lines.push(`Content-Type: ${contentType}; charset=UTF-8`);
|
|
@@ -32825,21 +33220,7 @@ function buildMimeMessage(options) {
|
|
|
32825
33220
|
lines.push("");
|
|
32826
33221
|
lines.push(`--${boundary}--`);
|
|
32827
33222
|
} else if (hasTextAlt) {
|
|
32828
|
-
lines.
|
|
32829
|
-
lines.push("");
|
|
32830
|
-
lines.push(`--${altBoundary}`);
|
|
32831
|
-
lines.push("Content-Type: text/plain; charset=UTF-8");
|
|
32832
|
-
lines.push("Content-Transfer-Encoding: 7bit");
|
|
32833
|
-
lines.push("");
|
|
32834
|
-
lines.push(options.text);
|
|
32835
|
-
lines.push("");
|
|
32836
|
-
lines.push(`--${altBoundary}`);
|
|
32837
|
-
lines.push("Content-Type: text/html; charset=UTF-8");
|
|
32838
|
-
lines.push("Content-Transfer-Encoding: 7bit");
|
|
32839
|
-
lines.push("");
|
|
32840
|
-
lines.push(options.body);
|
|
32841
|
-
lines.push("");
|
|
32842
|
-
lines.push(`--${altBoundary}--`);
|
|
33223
|
+
pushAlternativeBody(lines, altBoundary, options.text, options.body);
|
|
32843
33224
|
} else {
|
|
32844
33225
|
const contentType = options.html ? "text/html" : "text/plain";
|
|
32845
33226
|
lines.push(`Content-Type: ${contentType}; charset=UTF-8`);
|
|
@@ -33604,61 +33985,72 @@ function decodeEntities(raw) {
|
|
|
33604
33985
|
function parseXmlDocument(xml) {
|
|
33605
33986
|
const stack = [];
|
|
33606
33987
|
let root = null;
|
|
33607
|
-
let position = 0;
|
|
33608
33988
|
const addText = (text) => {
|
|
33609
33989
|
const current = stack[stack.length - 1];
|
|
33610
33990
|
if (current.children.length === 0) current.text += text;
|
|
33611
33991
|
};
|
|
33612
|
-
|
|
33613
|
-
const
|
|
33992
|
+
const skipComment = (at) => {
|
|
33993
|
+
const close = xml.indexOf("-->", at + 4);
|
|
33994
|
+
if (close === -1) malformed();
|
|
33995
|
+
return close + 3;
|
|
33996
|
+
};
|
|
33997
|
+
const readCData = (at) => {
|
|
33998
|
+
const close = xml.indexOf("]]>", at + 9);
|
|
33999
|
+
if (close === -1 || stack.length === 0) malformed();
|
|
34000
|
+
addText(xml.slice(at + 9, close));
|
|
34001
|
+
return close + 3;
|
|
34002
|
+
};
|
|
34003
|
+
const skipProcessingInstruction = (at) => {
|
|
34004
|
+
const close = xml.indexOf("?>", at + 2);
|
|
34005
|
+
if (close === -1) malformed();
|
|
34006
|
+
const target = xml.slice(at + 2, close).split(/\s/, 1)[0];
|
|
34007
|
+
if (target.toLowerCase() === "xml" && at !== 0) malformed();
|
|
34008
|
+
return close + 2;
|
|
34009
|
+
};
|
|
34010
|
+
const closeElement = (at) => {
|
|
34011
|
+
END_TAG.lastIndex = at;
|
|
34012
|
+
const match = END_TAG.exec(xml);
|
|
34013
|
+
const open = stack.pop();
|
|
34014
|
+
if (!match || !open || open.qualifiedName !== match[1]) malformed();
|
|
34015
|
+
return END_TAG.lastIndex;
|
|
34016
|
+
};
|
|
34017
|
+
const openElement = (at) => {
|
|
34018
|
+
START_TAG.lastIndex = at;
|
|
34019
|
+
const match = START_TAG.exec(xml);
|
|
34020
|
+
if (!match || stack.length === 0 && root !== null) malformed();
|
|
34021
|
+
for (const attribute of match[2].matchAll(ATTRIBUTE_VALUE)) decodeEntities(attribute[1] ?? attribute[2]);
|
|
34022
|
+
const qualifiedName = match[1];
|
|
34023
|
+
const element = {
|
|
34024
|
+
name: qualifiedName.slice(qualifiedName.indexOf(":") + 1),
|
|
34025
|
+
qualifiedName,
|
|
34026
|
+
children: [],
|
|
34027
|
+
text: ""
|
|
34028
|
+
};
|
|
34029
|
+
if (stack.length > 0) stack[stack.length - 1].children.push(element);
|
|
34030
|
+
else root = element;
|
|
34031
|
+
if (match[3] !== "/") stack.push(element);
|
|
34032
|
+
return START_TAG.lastIndex;
|
|
34033
|
+
};
|
|
34034
|
+
const readText = (at) => {
|
|
34035
|
+
const nextTag = xml.indexOf("<", at);
|
|
33614
34036
|
const textEnd = nextTag === -1 ? xml.length : nextTag;
|
|
33615
|
-
if (textEnd >
|
|
33616
|
-
const raw = xml.slice(
|
|
34037
|
+
if (textEnd > at) {
|
|
34038
|
+
const raw = xml.slice(at, textEnd);
|
|
33617
34039
|
if (stack.length > 0) addText(decodeEntities(raw));
|
|
33618
34040
|
else if (raw.trim() !== "") malformed();
|
|
33619
|
-
position = textEnd;
|
|
33620
|
-
if (nextTag === -1) break;
|
|
33621
|
-
}
|
|
33622
|
-
if (xml.startsWith("<!--", position)) {
|
|
33623
|
-
const close = xml.indexOf("-->", position + 4);
|
|
33624
|
-
if (close === -1) malformed();
|
|
33625
|
-
position = close + 3;
|
|
33626
|
-
} else if (xml.startsWith("<![CDATA[", position)) {
|
|
33627
|
-
const close = xml.indexOf("]]>", position + 9);
|
|
33628
|
-
if (close === -1 || stack.length === 0) malformed();
|
|
33629
|
-
addText(xml.slice(position + 9, close));
|
|
33630
|
-
position = close + 3;
|
|
33631
|
-
} else if (xml.startsWith("<?", position)) {
|
|
33632
|
-
const close = xml.indexOf("?>", position + 2);
|
|
33633
|
-
if (close === -1) malformed();
|
|
33634
|
-
const target = xml.slice(position + 2, close).split(/\s/, 1)[0];
|
|
33635
|
-
if (target.toLowerCase() === "xml" && position !== 0) malformed();
|
|
33636
|
-
position = close + 2;
|
|
33637
|
-
} else if (xml.startsWith("<!", position)) {
|
|
33638
|
-
malformed();
|
|
33639
|
-
} else if (xml.startsWith("</", position)) {
|
|
33640
|
-
END_TAG.lastIndex = position;
|
|
33641
|
-
const match = END_TAG.exec(xml);
|
|
33642
|
-
const open = stack.pop();
|
|
33643
|
-
if (!match || !open || open.qualifiedName !== match[1]) malformed();
|
|
33644
|
-
position = END_TAG.lastIndex;
|
|
33645
|
-
} else {
|
|
33646
|
-
START_TAG.lastIndex = position;
|
|
33647
|
-
const match = START_TAG.exec(xml);
|
|
33648
|
-
if (!match || stack.length === 0 && root !== null) malformed();
|
|
33649
|
-
for (const attribute of match[2].matchAll(ATTRIBUTE_VALUE)) decodeEntities(attribute[1] ?? attribute[2]);
|
|
33650
|
-
const qualifiedName = match[1];
|
|
33651
|
-
const element = {
|
|
33652
|
-
name: qualifiedName.slice(qualifiedName.indexOf(":") + 1),
|
|
33653
|
-
qualifiedName,
|
|
33654
|
-
children: [],
|
|
33655
|
-
text: ""
|
|
33656
|
-
};
|
|
33657
|
-
if (stack.length > 0) stack[stack.length - 1].children.push(element);
|
|
33658
|
-
else root = element;
|
|
33659
|
-
if (match[3] !== "/") stack.push(element);
|
|
33660
|
-
position = START_TAG.lastIndex;
|
|
33661
34041
|
}
|
|
34042
|
+
return textEnd;
|
|
34043
|
+
};
|
|
34044
|
+
let position = 0;
|
|
34045
|
+
while (position < xml.length) {
|
|
34046
|
+
position = readText(position);
|
|
34047
|
+
if (position >= xml.length) break;
|
|
34048
|
+
if (xml.startsWith("<!--", position)) position = skipComment(position);
|
|
34049
|
+
else if (xml.startsWith("<![CDATA[", position)) position = readCData(position);
|
|
34050
|
+
else if (xml.startsWith("<?", position)) position = skipProcessingInstruction(position);
|
|
34051
|
+
else if (xml.startsWith("<!", position)) malformed();
|
|
34052
|
+
else if (xml.startsWith("</", position)) position = closeElement(position);
|
|
34053
|
+
else position = openElement(position);
|
|
33662
34054
|
}
|
|
33663
34055
|
if (root === null || stack.length > 0) malformed();
|
|
33664
34056
|
return root;
|
|
@@ -35148,7 +35540,7 @@ import {
|
|
|
35148
35540
|
randomBytes as randomBytes10
|
|
35149
35541
|
} from "node:crypto";
|
|
35150
35542
|
function encodeBase64Url(value) {
|
|
35151
|
-
return Buffer.from(value).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(
|
|
35543
|
+
return Buffer.from(value).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/={1,2}$/, "");
|
|
35152
35544
|
}
|
|
35153
35545
|
function decodeBase64Url(value, name) {
|
|
35154
35546
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -35399,6 +35791,7 @@ var AiError, AiConfigError, AiTimeoutError, AiParseError, AiHTTPError, Ai, Aggre
|
|
|
35399
35791
|
var init_aiClient = __esm({
|
|
35400
35792
|
"../core/src/aiClient.ts"() {
|
|
35401
35793
|
"use strict";
|
|
35794
|
+
init_slashTrim();
|
|
35402
35795
|
init_api();
|
|
35403
35796
|
AiError = class extends Error {
|
|
35404
35797
|
};
|
|
@@ -35603,7 +35996,7 @@ var init_aiClient = __esm({
|
|
|
35603
35996
|
throw new AiConfigError("AI URL must be an http or https URL");
|
|
35604
35997
|
}
|
|
35605
35998
|
if (url.protocol !== "http:" && url.protocol !== "https:") throw new AiConfigError("AI URL must be an http or https URL");
|
|
35606
|
-
const path10 = url.pathname
|
|
35999
|
+
const path10 = rtrimSlashes(url.pathname);
|
|
35607
36000
|
if (path10 === "" || path10 === "/v1" || path10 === "/api" || path10 === "/v1beta/openai") {
|
|
35608
36001
|
const suffix = provider === "anthropic" ? "/messages" : capability === "embed" ? "/embeddings" : "/chat/completions";
|
|
35609
36002
|
url.pathname = (path10 || "/v1") + suffix;
|
|
@@ -37973,6 +38366,7 @@ __export(src_exports2, {
|
|
|
37973
38366
|
backgroundTaskCount: () => backgroundTaskCount,
|
|
37974
38367
|
buildEnvelope: () => buildEnvelope,
|
|
37975
38368
|
buildFrame: () => buildFrame,
|
|
38369
|
+
buildSeedFieldMap: () => buildSeedFieldMap,
|
|
37976
38370
|
buildSessionCookie: () => buildSessionCookie,
|
|
37977
38371
|
cacheBackendStats: () => cacheBackendStats,
|
|
37978
38372
|
cacheClear: () => cacheClear,
|
|
@@ -38078,6 +38472,7 @@ __export(src_exports2, {
|
|
|
38078
38472
|
parseFrame: () => parseFrame,
|
|
38079
38473
|
parseLineStream: () => parseLineStream,
|
|
38080
38474
|
parseMultipart: () => parseMultipart,
|
|
38475
|
+
parseSeedRequest: () => parseSeedRequest,
|
|
38081
38476
|
parseSseStream: () => parseSseStream,
|
|
38082
38477
|
parseUpgradeHeaders: () => parseUpgradeHeaders,
|
|
38083
38478
|
patch: () => patch,
|
|
@@ -42471,10 +42866,10 @@ __export(database_exports, {
|
|
|
42471
42866
|
stripTrailingSemicolons: () => stripTrailingSemicolons,
|
|
42472
42867
|
wrapWithCache: () => wrapWithCache
|
|
42473
42868
|
});
|
|
42474
|
-
import { AsyncLocalStorage as
|
|
42869
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
|
|
42475
42870
|
function stripTrailingSemicolons(sql) {
|
|
42476
42871
|
if (!sql) return sql;
|
|
42477
|
-
let stripped = sql.
|
|
42872
|
+
let stripped = sql.trimEnd();
|
|
42478
42873
|
while (stripped.endsWith(";")) {
|
|
42479
42874
|
stripped = stripped.slice(0, -1).replace(/\s+$/, "");
|
|
42480
42875
|
}
|
|
@@ -42641,6 +43036,13 @@ async function createAdapterFromUrl(url, username, password) {
|
|
|
42641
43036
|
}
|
|
42642
43037
|
async function buildAdapterFromUrl(url, username, password) {
|
|
42643
43038
|
const parsed = parseDatabaseUrl(url, username, password);
|
|
43039
|
+
const serverConfig = {
|
|
43040
|
+
host: parsed.host ?? void 0,
|
|
43041
|
+
port: parsed.port ?? void 0,
|
|
43042
|
+
user: parsed.username ?? void 0,
|
|
43043
|
+
password: parsed.password ?? void 0,
|
|
43044
|
+
database: parsed.database || void 0
|
|
43045
|
+
};
|
|
42644
43046
|
switch (parsed.engine) {
|
|
42645
43047
|
case "sqlite": {
|
|
42646
43048
|
const { SQLiteAdapter: SQLiteAdapter2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
|
|
@@ -42648,49 +43050,25 @@ async function buildAdapterFromUrl(url, username, password) {
|
|
|
42648
43050
|
}
|
|
42649
43051
|
case "postgres": {
|
|
42650
43052
|
const { PostgresAdapter: PostgresAdapter2 } = await Promise.resolve().then(() => (init_postgres(), postgres_exports));
|
|
42651
|
-
const adapter = new PostgresAdapter2(
|
|
42652
|
-
host: parsed.host ?? void 0,
|
|
42653
|
-
port: parsed.port ?? void 0,
|
|
42654
|
-
user: parsed.username ?? void 0,
|
|
42655
|
-
password: parsed.password ?? void 0,
|
|
42656
|
-
database: parsed.database || void 0
|
|
42657
|
-
});
|
|
43053
|
+
const adapter = new PostgresAdapter2(serverConfig);
|
|
42658
43054
|
await adapter.connect();
|
|
42659
43055
|
return adapter;
|
|
42660
43056
|
}
|
|
42661
43057
|
case "mysql": {
|
|
42662
43058
|
const { MysqlAdapter: MysqlAdapter2 } = await Promise.resolve().then(() => (init_mysql(), mysql_exports));
|
|
42663
|
-
const adapter = new MysqlAdapter2(
|
|
42664
|
-
host: parsed.host ?? void 0,
|
|
42665
|
-
port: parsed.port ?? void 0,
|
|
42666
|
-
user: parsed.username ?? void 0,
|
|
42667
|
-
password: parsed.password ?? void 0,
|
|
42668
|
-
database: parsed.database || void 0
|
|
42669
|
-
});
|
|
43059
|
+
const adapter = new MysqlAdapter2(serverConfig);
|
|
42670
43060
|
await adapter.connect();
|
|
42671
43061
|
return adapter;
|
|
42672
43062
|
}
|
|
42673
43063
|
case "mssql": {
|
|
42674
43064
|
const { MssqlAdapter: MssqlAdapter2 } = await Promise.resolve().then(() => (init_mssql(), mssql_exports));
|
|
42675
|
-
const adapter = new MssqlAdapter2(
|
|
42676
|
-
host: parsed.host ?? void 0,
|
|
42677
|
-
port: parsed.port ?? void 0,
|
|
42678
|
-
user: parsed.username ?? void 0,
|
|
42679
|
-
password: parsed.password ?? void 0,
|
|
42680
|
-
database: parsed.database || void 0
|
|
42681
|
-
});
|
|
43065
|
+
const adapter = new MssqlAdapter2(serverConfig);
|
|
42682
43066
|
await adapter.connect();
|
|
42683
43067
|
return adapter;
|
|
42684
43068
|
}
|
|
42685
43069
|
case "firebird": {
|
|
42686
43070
|
const { FirebirdAdapter: FirebirdAdapter2 } = await Promise.resolve().then(() => (init_firebird(), firebird_exports));
|
|
42687
|
-
const adapter = new FirebirdAdapter2(
|
|
42688
|
-
host: parsed.host ?? void 0,
|
|
42689
|
-
port: parsed.port ?? void 0,
|
|
42690
|
-
user: parsed.username ?? void 0,
|
|
42691
|
-
password: parsed.password ?? void 0,
|
|
42692
|
-
database: parsed.database || void 0
|
|
42693
|
-
});
|
|
43071
|
+
const adapter = new FirebirdAdapter2(serverConfig);
|
|
42694
43072
|
await adapter.connect();
|
|
42695
43073
|
return adapter;
|
|
42696
43074
|
}
|
|
@@ -42885,9 +43263,9 @@ var init_database = __esm({
|
|
|
42885
43263
|
leases = /* @__PURE__ */ new Map();
|
|
42886
43264
|
discardedSlots = /* @__PURE__ */ new Set();
|
|
42887
43265
|
repairs = /* @__PURE__ */ new Map();
|
|
42888
|
-
leaseOwner = new
|
|
42889
|
-
operationStore = new
|
|
42890
|
-
txStore = new
|
|
43266
|
+
leaseOwner = new AsyncLocalStorage4();
|
|
43267
|
+
operationStore = new AsyncLocalStorage4();
|
|
43268
|
+
txStore = new AsyncLocalStorage4();
|
|
42891
43269
|
/**
|
|
42892
43270
|
* Create a Database wrapping an existing adapter.
|
|
42893
43271
|
* For creating a Database from a URL, use the async static factories:
|
|
@@ -45627,7 +46005,7 @@ function tableData(opts) {
|
|
|
45627
46005
|
readonly: !opts.editable,
|
|
45628
46006
|
inline_script: opts.inlineScript,
|
|
45629
46007
|
table_name: h(opts.tableName),
|
|
45630
|
-
|
|
46008
|
+
table_id: opts.tableId ? String(opts.tableId) : ""
|
|
45631
46009
|
};
|
|
45632
46010
|
}
|
|
45633
46011
|
var init_crudTable = __esm({
|
|
@@ -45668,13 +46046,13 @@ function renderModalForm(mode, columns, pk, edit) {
|
|
|
45668
46046
|
value: "",
|
|
45669
46047
|
placeholder: h(`Enter ${label.toLowerCase()}`),
|
|
45670
46048
|
type: "text",
|
|
45671
|
-
|
|
46049
|
+
required: false,
|
|
45672
46050
|
input: true
|
|
45673
46051
|
};
|
|
45674
46052
|
});
|
|
45675
46053
|
return renderCrudTemplate("form", {
|
|
45676
46054
|
wrap: true,
|
|
45677
|
-
|
|
46055
|
+
form_id: `form-${mode}`,
|
|
45678
46056
|
action: "",
|
|
45679
46057
|
form_method: "POST",
|
|
45680
46058
|
method_override: null,
|
|
@@ -45731,7 +46109,7 @@ function fieldInputShape(field, base) {
|
|
|
45731
46109
|
case "text":
|
|
45732
46110
|
return { ...base, textarea: true };
|
|
45733
46111
|
case "boolean":
|
|
45734
|
-
return { ...base, checkbox: true,
|
|
46112
|
+
return { ...base, checkbox: true, checked: !!value };
|
|
45735
46113
|
case "select":
|
|
45736
46114
|
return { ...base, select: true, options_html: buildOptions(field.options, value) };
|
|
45737
46115
|
case "date":
|
|
@@ -45754,7 +46132,7 @@ function buildCustomField(field) {
|
|
|
45754
46132
|
label: h(String(label)),
|
|
45755
46133
|
value: h(field.value ?? ""),
|
|
45756
46134
|
placeholder: "",
|
|
45757
|
-
|
|
46135
|
+
required: !!field.required
|
|
45758
46136
|
};
|
|
45759
46137
|
return fieldInputShape(field, base);
|
|
45760
46138
|
}
|
|
@@ -45762,7 +46140,7 @@ function generateForm2(fields, options = {}) {
|
|
|
45762
46140
|
const verb = String(options.method ?? "POST").toUpperCase();
|
|
45763
46141
|
return renderCrudTemplate("form", {
|
|
45764
46142
|
wrap: true,
|
|
45765
|
-
|
|
46143
|
+
form_id: "",
|
|
45766
46144
|
action: h(options.action ?? "/"),
|
|
45767
46145
|
form_method: h(verb),
|
|
45768
46146
|
method_override: ["PUT", "PATCH", "DELETE"].includes(verb) ? verb : null,
|
|
@@ -49882,20 +50260,210 @@ var init_attachment = __esm({
|
|
|
49882
50260
|
}
|
|
49883
50261
|
});
|
|
49884
50262
|
|
|
50263
|
+
// src/realtime/s3Client.ts
|
|
50264
|
+
import { createHash as createHash11, createHmac as createHmac4 } from "node:crypto";
|
|
50265
|
+
function retryAfterSeconds(raw) {
|
|
50266
|
+
if (raw == null) return null;
|
|
50267
|
+
const n = Number(raw);
|
|
50268
|
+
return Number.isFinite(n) ? Math.max(0, n) : null;
|
|
50269
|
+
}
|
|
50270
|
+
function uriEncode(value, isPath) {
|
|
50271
|
+
let out = "";
|
|
50272
|
+
for (const byte of Buffer.from(value, "utf8")) {
|
|
50273
|
+
const ch = String.fromCharCode(byte);
|
|
50274
|
+
if (UNRESERVED.test(ch) || isPath && ch === "/") out += ch;
|
|
50275
|
+
else out += "%" + byte.toString(16).toUpperCase().padStart(2, "0");
|
|
50276
|
+
}
|
|
50277
|
+
return out;
|
|
50278
|
+
}
|
|
50279
|
+
function sha256Hex2(data) {
|
|
50280
|
+
return createHash11("sha256").update(data).digest("hex");
|
|
50281
|
+
}
|
|
50282
|
+
function hmac2(key, msg) {
|
|
50283
|
+
return createHmac4("sha256", key).update(msg, "utf8").digest();
|
|
50284
|
+
}
|
|
50285
|
+
var ALGORITHM, SERVICE, UNRESERVED, MAX_RETRIES, BACKOFF_BASE, BACKOFF_CAP, S3Error, S3ConfigError, sleep, S3Client;
|
|
50286
|
+
var init_s3Client = __esm({
|
|
50287
|
+
"src/realtime/s3Client.ts"() {
|
|
50288
|
+
"use strict";
|
|
50289
|
+
ALGORITHM = "AWS4-HMAC-SHA256";
|
|
50290
|
+
SERVICE = "s3";
|
|
50291
|
+
UNRESERVED = /[A-Za-z0-9\-_.~]/;
|
|
50292
|
+
MAX_RETRIES = 2;
|
|
50293
|
+
BACKOFF_BASE = 0.5;
|
|
50294
|
+
BACKOFF_CAP = 20;
|
|
50295
|
+
S3Error = class extends Error {
|
|
50296
|
+
constructor(status2, message) {
|
|
50297
|
+
super(message);
|
|
50298
|
+
this.status = status2;
|
|
50299
|
+
this.name = "S3Error";
|
|
50300
|
+
}
|
|
50301
|
+
status;
|
|
50302
|
+
/** Seconds from a Retry-After header in its delta-seconds form, else null. */
|
|
50303
|
+
retryAfter = null;
|
|
50304
|
+
};
|
|
50305
|
+
S3ConfigError = class extends Error {
|
|
50306
|
+
constructor(message) {
|
|
50307
|
+
super(message);
|
|
50308
|
+
this.name = "S3ConfigError";
|
|
50309
|
+
}
|
|
50310
|
+
};
|
|
50311
|
+
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
50312
|
+
S3Client = class {
|
|
50313
|
+
scheme;
|
|
50314
|
+
host;
|
|
50315
|
+
// authority (host[:port]) — matches the Host header
|
|
50316
|
+
prefix;
|
|
50317
|
+
// path an endpoint carries (https://host/s3 -> "/s3"), "" if none
|
|
50318
|
+
region;
|
|
50319
|
+
accessKey;
|
|
50320
|
+
secretKey;
|
|
50321
|
+
timeout;
|
|
50322
|
+
constructor(opts = {}) {
|
|
50323
|
+
this.region = opts.region || "us-east-1";
|
|
50324
|
+
this.accessKey = opts.accessKey || "";
|
|
50325
|
+
this.secretKey = opts.secretKey || "";
|
|
50326
|
+
this.timeout = opts.timeout ?? 3e4;
|
|
50327
|
+
let base = opts.endpoint || `https://s3.${this.region}.amazonaws.com`;
|
|
50328
|
+
while (base.endsWith("/")) base = base.slice(0, -1);
|
|
50329
|
+
const m = /^([a-z][a-z0-9+.-]*):\/\/([^/]+)(\/.*)?$/i.exec(base);
|
|
50330
|
+
this.scheme = m ? m[1] : "https";
|
|
50331
|
+
this.host = m ? m[2] : base;
|
|
50332
|
+
let p = m && m[3] ? m[3] : "";
|
|
50333
|
+
while (p.endsWith("/")) p = p.slice(0, -1);
|
|
50334
|
+
this.prefix = p;
|
|
50335
|
+
}
|
|
50336
|
+
// -- public API (the subset S3Storage uses) --------------------------------
|
|
50337
|
+
async putObject(bucket, key, data, contentType = "application/octet-stream") {
|
|
50338
|
+
const body = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
50339
|
+
await this.request("PUT", this.canonicalUri(bucket, key), body, { "content-type": contentType });
|
|
50340
|
+
}
|
|
50341
|
+
async getObject(bucket, key) {
|
|
50342
|
+
return (await this.request("GET", this.canonicalUri(bucket, key), Buffer.alloc(0))).body;
|
|
50343
|
+
}
|
|
50344
|
+
async deleteObject(bucket, key) {
|
|
50345
|
+
await this.request("DELETE", this.canonicalUri(bucket, key), Buffer.alloc(0));
|
|
50346
|
+
}
|
|
50347
|
+
async headObject(bucket, key) {
|
|
50348
|
+
await this.request("HEAD", this.canonicalUri(bucket, key), Buffer.alloc(0));
|
|
50349
|
+
}
|
|
50350
|
+
/** Create the bucket (path-style `PUT /<bucket>`); an already-owned bucket (409) is success. */
|
|
50351
|
+
async createBucket(bucket) {
|
|
50352
|
+
try {
|
|
50353
|
+
await this.request("PUT", `${this.prefix}/${uriEncode(bucket, true)}`, Buffer.alloc(0));
|
|
50354
|
+
} catch (e) {
|
|
50355
|
+
if (!(e instanceof S3Error) || e.status !== 409) throw e;
|
|
50356
|
+
}
|
|
50357
|
+
}
|
|
50358
|
+
/** A URL that fetches the object directly, signed via SigV4 query params. */
|
|
50359
|
+
presignedGetUrl(bucket, key, expires = 3600) {
|
|
50360
|
+
this.requireCredentials();
|
|
50361
|
+
const { amzDate, dateStamp } = this.now();
|
|
50362
|
+
const uri = this.canonicalUri(bucket, key);
|
|
50363
|
+
const scope = `${dateStamp}/${this.region}/${SERVICE}/aws4_request`;
|
|
50364
|
+
const query = {
|
|
50365
|
+
"X-Amz-Algorithm": ALGORITHM,
|
|
50366
|
+
"X-Amz-Credential": `${this.accessKey}/${scope}`,
|
|
50367
|
+
"X-Amz-Date": amzDate,
|
|
50368
|
+
"X-Amz-Expires": String(expires),
|
|
50369
|
+
"X-Amz-SignedHeaders": "host"
|
|
50370
|
+
};
|
|
50371
|
+
const canonicalQs = this.canonicalQuery(query);
|
|
50372
|
+
const canonicalRequest = ["GET", uri, canonicalQs, `host:${this.host}
|
|
50373
|
+
`, "host", "UNSIGNED-PAYLOAD"].join("\n");
|
|
50374
|
+
const signature = this.sign(canonicalRequest, amzDate, dateStamp, scope);
|
|
50375
|
+
return `${this.scheme}://${this.host}${uri}?${canonicalQs}&X-Amz-Signature=${signature}`;
|
|
50376
|
+
}
|
|
50377
|
+
// -- signing + transport ----------------------------------------------------
|
|
50378
|
+
requireCredentials() {
|
|
50379
|
+
if (!this.accessKey || !this.secretKey) {
|
|
50380
|
+
throw new S3ConfigError(
|
|
50381
|
+
"S3 credentials are not set: set TINA4_STORAGE_KEY and TINA4_STORAGE_SECRET"
|
|
50382
|
+
);
|
|
50383
|
+
}
|
|
50384
|
+
}
|
|
50385
|
+
now() {
|
|
50386
|
+
const iso2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:-]|\.\d{3}/g, "");
|
|
50387
|
+
return { amzDate: iso2, dateStamp: iso2.slice(0, 8) };
|
|
50388
|
+
}
|
|
50389
|
+
canonicalUri(bucket, key) {
|
|
50390
|
+
return `${this.prefix}/${uriEncode(bucket, true)}/${uriEncode(key, true)}`;
|
|
50391
|
+
}
|
|
50392
|
+
canonicalQuery(query) {
|
|
50393
|
+
return Object.keys(query).sort().map((k) => `${uriEncode(k, false)}=${uriEncode(query[k], false)}`).join("&");
|
|
50394
|
+
}
|
|
50395
|
+
signingKey(dateStamp) {
|
|
50396
|
+
const kDate = hmac2(`AWS4${this.secretKey}`, dateStamp);
|
|
50397
|
+
const kRegion = hmac2(kDate, this.region);
|
|
50398
|
+
const kService = hmac2(kRegion, SERVICE);
|
|
50399
|
+
return hmac2(kService, "aws4_request");
|
|
50400
|
+
}
|
|
50401
|
+
sign(canonicalRequest, amzDate, dateStamp, scope) {
|
|
50402
|
+
const stringToSign = [ALGORITHM, amzDate, scope, sha256Hex2(canonicalRequest)].join("\n");
|
|
50403
|
+
return createHmac4("sha256", this.signingKey(dateStamp)).update(stringToSign, "utf8").digest("hex");
|
|
50404
|
+
}
|
|
50405
|
+
async request(method, uri, body, extraHeaders = {}) {
|
|
50406
|
+
this.requireCredentials();
|
|
50407
|
+
for (let attempt = 0; ; attempt++) {
|
|
50408
|
+
const last = attempt === MAX_RETRIES;
|
|
50409
|
+
let delay2 = null;
|
|
50410
|
+
try {
|
|
50411
|
+
return await this.send(method, uri, body, extraHeaders);
|
|
50412
|
+
} catch (e) {
|
|
50413
|
+
if (e instanceof S3Error) {
|
|
50414
|
+
if (last || !(e.status === 429 || e.status >= 500)) throw e;
|
|
50415
|
+
delay2 = e.retryAfter;
|
|
50416
|
+
} else {
|
|
50417
|
+
if (last) throw e;
|
|
50418
|
+
delay2 = null;
|
|
50419
|
+
}
|
|
50420
|
+
}
|
|
50421
|
+
if (delay2 == null) delay2 = Math.random() * (BACKOFF_BASE * 2 ** attempt);
|
|
50422
|
+
await sleep(Math.min(delay2, BACKOFF_CAP) * 1e3);
|
|
50423
|
+
}
|
|
50424
|
+
}
|
|
50425
|
+
async send(method, uri, body, extraHeaders = {}) {
|
|
50426
|
+
const { amzDate, dateStamp } = this.now();
|
|
50427
|
+
const payloadHash = sha256Hex2(body);
|
|
50428
|
+
const signed = {
|
|
50429
|
+
host: this.host,
|
|
50430
|
+
"x-amz-content-sha256": payloadHash,
|
|
50431
|
+
"x-amz-date": amzDate
|
|
50432
|
+
};
|
|
50433
|
+
const signedKeys = Object.keys(signed).sort();
|
|
50434
|
+
const canonicalHeaders = signedKeys.map((k) => `${k}:${signed[k]}
|
|
50435
|
+
`).join("");
|
|
50436
|
+
const signedHeaders = signedKeys.join(";");
|
|
50437
|
+
const canonicalRequest = [method, uri, "", canonicalHeaders, signedHeaders, payloadHash].join("\n");
|
|
50438
|
+
const scope = `${dateStamp}/${this.region}/${SERVICE}/aws4_request`;
|
|
50439
|
+
const signature = this.sign(canonicalRequest, amzDate, dateStamp, scope);
|
|
50440
|
+
const authorization = `${ALGORITHM} Credential=${this.accessKey}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
50441
|
+
const headers = {
|
|
50442
|
+
Authorization: authorization,
|
|
50443
|
+
"x-amz-content-sha256": payloadHash,
|
|
50444
|
+
"x-amz-date": amzDate,
|
|
50445
|
+
...extraHeaders
|
|
50446
|
+
};
|
|
50447
|
+
const url = `${this.scheme}://${this.host}${uri}`;
|
|
50448
|
+
const init = { method, headers, redirect: "manual" };
|
|
50449
|
+
if (method === "PUT" || method === "POST") init.body = body;
|
|
50450
|
+
if (AbortSignal.timeout) init.signal = AbortSignal.timeout(this.timeout);
|
|
50451
|
+
const response = await fetch(url, init);
|
|
50452
|
+
if (response.status < 200 || response.status >= 300) {
|
|
50453
|
+
const err = new S3Error(response.status, `${method} ${uri}: HTTP ${response.status}`);
|
|
50454
|
+
err.retryAfter = retryAfterSeconds(response.headers.get("retry-after"));
|
|
50455
|
+
throw err;
|
|
50456
|
+
}
|
|
50457
|
+
return { status: response.status, body: Buffer.from(await response.arrayBuffer()) };
|
|
50458
|
+
}
|
|
50459
|
+
};
|
|
50460
|
+
}
|
|
50461
|
+
});
|
|
50462
|
+
|
|
49885
50463
|
// src/realtime/storage.ts
|
|
49886
50464
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
49887
|
-
import { mkdirSync as mkdirSync21, readFileSync as readFileSync26, writeFileSync as writeFileSync18, unlinkSync as
|
|
50465
|
+
import { mkdirSync as mkdirSync21, readFileSync as readFileSync26, writeFileSync as writeFileSync18, unlinkSync as unlinkSync9, statSync as statSync19 } from "node:fs";
|
|
49888
50466
|
import { resolve as resolve20, sep as sep8 } from "node:path";
|
|
49889
|
-
import { createRequire as createRequire8 } from "node:module";
|
|
49890
|
-
function loadAwsSdk(packageName) {
|
|
49891
|
-
try {
|
|
49892
|
-
return require2(packageName);
|
|
49893
|
-
} catch (error2) {
|
|
49894
|
-
const missing = error2?.code === "MODULE_NOT_FOUND" && String(error2?.message ?? "").includes(`'${packageName}'`);
|
|
49895
|
-
if (!missing) throw error2;
|
|
49896
|
-
throw optionalPackageMissing("S3Storage", S3_PACKAGES);
|
|
49897
|
-
}
|
|
49898
|
-
}
|
|
49899
50467
|
function storageKey(filename = "") {
|
|
49900
50468
|
let ext = "";
|
|
49901
50469
|
if (filename && filename.includes(".")) {
|
|
@@ -49920,14 +50488,12 @@ function selectStorage(storage) {
|
|
|
49920
50488
|
}
|
|
49921
50489
|
return new LocalStorage();
|
|
49922
50490
|
}
|
|
49923
|
-
var
|
|
50491
|
+
var UNSAFE, LocalStorage, S3Storage;
|
|
49924
50492
|
var init_storage = __esm({
|
|
49925
50493
|
"src/realtime/storage.ts"() {
|
|
49926
50494
|
"use strict";
|
|
49927
50495
|
init_src2();
|
|
49928
|
-
|
|
49929
|
-
require2 = createRequire8(import.meta.url);
|
|
49930
|
-
S3_PACKAGES = ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"];
|
|
50496
|
+
init_s3Client();
|
|
49931
50497
|
UNSAFE = /[^A-Za-z0-9]/g;
|
|
49932
50498
|
LocalStorage = class {
|
|
49933
50499
|
directory;
|
|
@@ -49959,7 +50525,7 @@ var init_storage = __esm({
|
|
|
49959
50525
|
}
|
|
49960
50526
|
delete(key) {
|
|
49961
50527
|
try {
|
|
49962
|
-
|
|
50528
|
+
unlinkSync9(this.pathFor(key));
|
|
49963
50529
|
} catch {
|
|
49964
50530
|
}
|
|
49965
50531
|
}
|
|
@@ -49977,51 +50543,36 @@ var init_storage = __esm({
|
|
|
49977
50543
|
constructor(opts = {}) {
|
|
49978
50544
|
this.bucket = opts.bucket || process.env.TINA4_STORAGE_BUCKET || "";
|
|
49979
50545
|
if (!this.bucket) throw new Error("S3Storage requires TINA4_STORAGE_BUCKET");
|
|
49980
|
-
const { S3Client } = loadAwsSdk("@aws-sdk/client-s3");
|
|
49981
|
-
loadAwsSdk("@aws-sdk/s3-request-presigner");
|
|
49982
|
-
const endpoint = opts.endpoint || process.env.TINA4_STORAGE_URL;
|
|
49983
50546
|
this.client = new S3Client({
|
|
49984
|
-
endpoint: endpoint || void 0,
|
|
49985
|
-
forcePathStyle: true,
|
|
50547
|
+
endpoint: opts.endpoint || process.env.TINA4_STORAGE_URL || void 0,
|
|
49986
50548
|
region: opts.region || process.env.TINA4_STORAGE_REGION || "us-east-1",
|
|
49987
|
-
|
|
49988
|
-
|
|
49989
|
-
secretAccessKey: opts.secret || process.env.TINA4_STORAGE_SECRET || ""
|
|
49990
|
-
}
|
|
50549
|
+
accessKey: opts.key || process.env.TINA4_STORAGE_KEY || "",
|
|
50550
|
+
secretKey: opts.secret || process.env.TINA4_STORAGE_SECRET || ""
|
|
49991
50551
|
});
|
|
49992
50552
|
}
|
|
49993
50553
|
async put(key, data, mime = "application/octet-stream") {
|
|
49994
|
-
|
|
49995
|
-
await this.client.send(new PutObjectCommand({ Bucket: this.bucket, Key: key, Body: data, ContentType: mime }));
|
|
50554
|
+
await this.client.putObject(this.bucket, key, data, mime);
|
|
49996
50555
|
}
|
|
49997
50556
|
async get(key) {
|
|
49998
50557
|
try {
|
|
49999
|
-
|
|
50000
|
-
const res = await this.client.send(new GetObjectCommand({ Bucket: this.bucket, Key: key }));
|
|
50001
|
-
const chunks = [];
|
|
50002
|
-
for await (const chunk of res.Body) chunks.push(Buffer.from(chunk));
|
|
50003
|
-
return Buffer.concat(chunks);
|
|
50558
|
+
return await this.client.getObject(this.bucket, key);
|
|
50004
50559
|
} catch {
|
|
50005
50560
|
return null;
|
|
50006
50561
|
}
|
|
50007
50562
|
}
|
|
50008
50563
|
async url(key, ttl = 3600) {
|
|
50009
|
-
|
|
50010
|
-
const { getSignedUrl } = loadAwsSdk("@aws-sdk/s3-request-presigner");
|
|
50011
|
-
return getSignedUrl(this.client, new GetObjectCommand({ Bucket: this.bucket, Key: key }), { expiresIn: ttl });
|
|
50564
|
+
return this.client.presignedGetUrl(this.bucket, key, ttl);
|
|
50012
50565
|
}
|
|
50013
50566
|
async delete(key) {
|
|
50014
50567
|
try {
|
|
50015
|
-
|
|
50016
|
-
await this.client.send(new DeleteObjectCommand({ Bucket: this.bucket, Key: key }));
|
|
50568
|
+
await this.client.deleteObject(this.bucket, key);
|
|
50017
50569
|
} catch (e) {
|
|
50018
50570
|
Log.error(`S3Storage delete failed for ${key}: ${e.message}`);
|
|
50019
50571
|
}
|
|
50020
50572
|
}
|
|
50021
50573
|
async exists(key) {
|
|
50022
50574
|
try {
|
|
50023
|
-
|
|
50024
|
-
await this.client.send(new HeadObjectCommand({ Bucket: this.bucket, Key: key }));
|
|
50575
|
+
await this.client.headObject(this.bucket, key);
|
|
50025
50576
|
return true;
|
|
50026
50577
|
} catch {
|
|
50027
50578
|
return false;
|
|
@@ -50032,7 +50583,7 @@ var init_storage = __esm({
|
|
|
50032
50583
|
});
|
|
50033
50584
|
|
|
50034
50585
|
// src/realtime/realtime.ts
|
|
50035
|
-
import { createHmac as
|
|
50586
|
+
import { createHmac as createHmac5 } from "node:crypto";
|
|
50036
50587
|
function iceServers() {
|
|
50037
50588
|
const stun = process.env.TINA4_RTC_STUN_URLS || DEFAULT_STUN;
|
|
50038
50589
|
const servers = [{ urls: splitUrls(stun) }];
|
|
@@ -50041,14 +50592,14 @@ function iceServers() {
|
|
|
50041
50592
|
if (turnUrl && secret) {
|
|
50042
50593
|
const ttl = parseInt(process.env.TINA4_RTC_TURN_TTL || "3600", 10);
|
|
50043
50594
|
const username = String(Math.floor(Date.now() / 1e3) + ttl);
|
|
50044
|
-
const credential =
|
|
50595
|
+
const credential = createHmac5("sha1", secret).update(username).digest("base64");
|
|
50045
50596
|
servers.push({ urls: splitUrls(turnUrl), username, credential });
|
|
50046
50597
|
}
|
|
50047
50598
|
return servers;
|
|
50048
50599
|
}
|
|
50049
50600
|
async function realtime(options = {}) {
|
|
50050
50601
|
const features = options.features ?? ["calls"];
|
|
50051
|
-
const raw = (options.prefix ?? "")
|
|
50602
|
+
const raw = trimSlashes(options.prefix ?? "");
|
|
50052
50603
|
const p = raw ? `/${raw}` : "";
|
|
50053
50604
|
_authorize = options.authorize;
|
|
50054
50605
|
if (features.includes("chat") || features.includes("files")) {
|
|
@@ -50320,6 +50871,7 @@ var DEFAULT_STUN, _authorize;
|
|
|
50320
50871
|
var init_realtime = __esm({
|
|
50321
50872
|
"src/realtime/realtime.ts"() {
|
|
50322
50873
|
"use strict";
|
|
50874
|
+
init_slashTrim();
|
|
50323
50875
|
init_src2();
|
|
50324
50876
|
init_workspace();
|
|
50325
50877
|
init_channel();
|
|
@@ -50337,6 +50889,7 @@ var init_realtime2 = __esm({
|
|
|
50337
50889
|
"use strict";
|
|
50338
50890
|
init_realtime();
|
|
50339
50891
|
init_storage();
|
|
50892
|
+
init_s3Client();
|
|
50340
50893
|
init_workspace();
|
|
50341
50894
|
init_channel();
|
|
50342
50895
|
init_channelMember();
|