@adep/cli 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +346 -62
- package/dist/vite.js +323 -47
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1687,6 +1687,7 @@ function stripQuotes(raw) {
|
|
|
1687
1687
|
const t = raw.trim();
|
|
1688
1688
|
if (/^'.*'$/.test(t)) return t.slice(1, -1);
|
|
1689
1689
|
if (/^".*"$/.test(t)) return t.slice(1, -1);
|
|
1690
|
+
if (/^NULL$/i.test(t)) return null;
|
|
1690
1691
|
return t;
|
|
1691
1692
|
}
|
|
1692
1693
|
function parseCreateTable(ddl) {
|
|
@@ -1707,50 +1708,109 @@ function parseCreateTable(ddl) {
|
|
|
1707
1708
|
}
|
|
1708
1709
|
return { name, columns };
|
|
1709
1710
|
}
|
|
1710
|
-
function
|
|
1711
|
-
const
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
const inner = rhs.replace(/^\(|\)$/g, "");
|
|
1734
|
-
const items = splitListItems(inner);
|
|
1735
|
-
const list = items.map((item) => {
|
|
1736
|
-
if (item.trim() === "?") return cursor.take();
|
|
1737
|
-
return stripQuotes(item);
|
|
1738
|
-
});
|
|
1739
|
-
clauses.push({ column: col, operator: op, value: list, list: true });
|
|
1711
|
+
function splitTopLevel2(raw, token) {
|
|
1712
|
+
const parts = [];
|
|
1713
|
+
let depth = 0;
|
|
1714
|
+
let inQuote = false;
|
|
1715
|
+
let buffer = "";
|
|
1716
|
+
const n = raw.length;
|
|
1717
|
+
const isTokenAt = (i) => {
|
|
1718
|
+
const word = raw.slice(i, i + token.length);
|
|
1719
|
+
if (word.toUpperCase() !== token) return false;
|
|
1720
|
+
const before = i === 0 ? "" : raw[i - 1] ?? "";
|
|
1721
|
+
const after = raw[i + token.length] ?? "";
|
|
1722
|
+
return !/[A-Za-z0-9_.]/.test(before) && !/[A-Za-z0-9_.]/.test(after);
|
|
1723
|
+
};
|
|
1724
|
+
for (let i = 0; i < n; i += 1) {
|
|
1725
|
+
const ch = raw[i] ?? "";
|
|
1726
|
+
if (ch === "'") {
|
|
1727
|
+
if (inQuote && raw[i + 1] === "'") {
|
|
1728
|
+
buffer += ch + (raw[i + 1] ?? "");
|
|
1729
|
+
i += 1;
|
|
1730
|
+
continue;
|
|
1731
|
+
}
|
|
1732
|
+
inQuote = !inQuote;
|
|
1733
|
+
buffer += ch;
|
|
1740
1734
|
continue;
|
|
1741
1735
|
}
|
|
1742
|
-
|
|
1743
|
-
if (
|
|
1744
|
-
|
|
1736
|
+
if (!inQuote && ch === "(") depth += 1;
|
|
1737
|
+
else if (!inQuote && ch === ")") depth -= 1;
|
|
1738
|
+
if (!inQuote && depth === 0 && isTokenAt(i)) {
|
|
1739
|
+
parts.push(buffer);
|
|
1740
|
+
buffer = "";
|
|
1741
|
+
i += token.length - 1;
|
|
1745
1742
|
} else {
|
|
1746
|
-
|
|
1743
|
+
buffer += ch;
|
|
1747
1744
|
}
|
|
1748
|
-
clauses.push({ column: col, operator: op, value, list: false });
|
|
1749
1745
|
}
|
|
1750
|
-
|
|
1746
|
+
parts.push(buffer);
|
|
1747
|
+
return parts;
|
|
1751
1748
|
}
|
|
1752
|
-
function
|
|
1753
|
-
|
|
1749
|
+
function unqualifyColumn(raw) {
|
|
1750
|
+
const t = raw.trim();
|
|
1751
|
+
const dot = t.lastIndexOf(".");
|
|
1752
|
+
if (dot > 0 && /^[A-Za-z_][A-Za-z0-9_]*\./.test(t)) return t.slice(dot + 1);
|
|
1753
|
+
return t;
|
|
1754
|
+
}
|
|
1755
|
+
function parseLeftJoins(tail) {
|
|
1756
|
+
const joins = [];
|
|
1757
|
+
const re = /LEFT\s+JOIN\s+((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+([A-Za-z_][A-Za-z0-9_]*)\s+ON\s+([A-Za-z_][A-Za-z0-9_.]*)\s*=\s*([A-Za-z_][A-Za-z0-9_.]*)/gi;
|
|
1758
|
+
let m;
|
|
1759
|
+
while ((m = re.exec(tail)) !== null) {
|
|
1760
|
+
joins.push({
|
|
1761
|
+
table: ident(m[1] ?? ""),
|
|
1762
|
+
alias: m[3] ?? "",
|
|
1763
|
+
leftCol: unqualifyColumn(m[4] ?? ""),
|
|
1764
|
+
rightCol: unqualifyColumn(m[5] ?? "")
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
return joins;
|
|
1768
|
+
}
|
|
1769
|
+
function parseWhereClauses(whereRaw, cursor) {
|
|
1770
|
+
const parseAndGroup = (andRaw) => {
|
|
1771
|
+
const clauses = [];
|
|
1772
|
+
for (const part of splitTopLevel2(andRaw, "AND")) {
|
|
1773
|
+
const t = part.trim();
|
|
1774
|
+
if (t.length === 0) continue;
|
|
1775
|
+
if (/\bIS\s+NULL\b/i.test(t)) {
|
|
1776
|
+
clauses.push({ column: unqualifyColumn(ident(t.split(/\s+IS\s+NULL\b/i)[0] ?? "")), operator: "IS NULL", value: null, list: false });
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
1779
|
+
if (/\bIS\s+NOT\s+NULL\b/i.test(t)) {
|
|
1780
|
+
clauses.push({ column: unqualifyColumn(ident(t.split(/\s+IS\s+NOT\s+NULL\b/i)[0] ?? "")), operator: "IS NOT NULL", value: null, list: false });
|
|
1781
|
+
continue;
|
|
1782
|
+
}
|
|
1783
|
+
const opMatch = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_.]*))\s*(=|!=|>=|<=|>|<|like|in|not\s+in)\s*(.+)$/i.exec(
|
|
1784
|
+
t
|
|
1785
|
+
);
|
|
1786
|
+
if (opMatch === null) continue;
|
|
1787
|
+
const col = unqualifyColumn(ident(opMatch[1] ?? ""));
|
|
1788
|
+
const op = (opMatch[3] ?? opMatch[2] ?? "").toLowerCase();
|
|
1789
|
+
const rhs = (opMatch[4] ?? "").trim();
|
|
1790
|
+
if (op === "in" || op === "not in") {
|
|
1791
|
+
const inner = rhs.replace(/^\(|\)$/g, "");
|
|
1792
|
+
const items = splitListItems(inner);
|
|
1793
|
+
const list = items.map((item) => {
|
|
1794
|
+
if (item.trim() === "?") return cursor.take();
|
|
1795
|
+
return stripQuotes(item);
|
|
1796
|
+
});
|
|
1797
|
+
clauses.push({ column: col, operator: op, value: list, list: true });
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
let value;
|
|
1801
|
+
if (rhs === "?") {
|
|
1802
|
+
value = cursor.take();
|
|
1803
|
+
} else {
|
|
1804
|
+
value = stripQuotes(rhs);
|
|
1805
|
+
}
|
|
1806
|
+
clauses.push({ column: col, operator: op, value, list: false });
|
|
1807
|
+
}
|
|
1808
|
+
return clauses;
|
|
1809
|
+
};
|
|
1810
|
+
return splitTopLevel2(whereRaw, "OR").map((group) => group.trim()).filter((group) => group.length > 0).map(parseAndGroup);
|
|
1811
|
+
}
|
|
1812
|
+
function matchWhere(groups, row) {
|
|
1813
|
+
return groups.some((clauses) => clauses.every((c) => matchValue(row[c.column], c)));
|
|
1754
1814
|
}
|
|
1755
1815
|
function splitListItems(inner) {
|
|
1756
1816
|
const items = [];
|
|
@@ -1770,6 +1830,153 @@ function splitListItems(inner) {
|
|
|
1770
1830
|
items.push(buffer);
|
|
1771
1831
|
return items;
|
|
1772
1832
|
}
|
|
1833
|
+
function resolveSetValue(value, existing, row) {
|
|
1834
|
+
if (value instanceof ExcludedRef) return row[value.column];
|
|
1835
|
+
if (value instanceof ColumnRef) return existing[value.column];
|
|
1836
|
+
if (value instanceof ExprRef) return evalArithmetic(value.source, existing, row);
|
|
1837
|
+
return value;
|
|
1838
|
+
}
|
|
1839
|
+
function evalArithmetic(src, existing, row) {
|
|
1840
|
+
const num = (v) => {
|
|
1841
|
+
const n2 = Number(v);
|
|
1842
|
+
return Number.isFinite(n2) ? n2 : 0;
|
|
1843
|
+
};
|
|
1844
|
+
let i = 0;
|
|
1845
|
+
const n = src.length;
|
|
1846
|
+
const skip = () => {
|
|
1847
|
+
while (i < n && /\s/.test(src[i] ?? "")) i += 1;
|
|
1848
|
+
};
|
|
1849
|
+
const peek = () => {
|
|
1850
|
+
skip();
|
|
1851
|
+
return i < n ? src[i] ?? "" : null;
|
|
1852
|
+
};
|
|
1853
|
+
const parseIdent = () => {
|
|
1854
|
+
skip();
|
|
1855
|
+
const m = /^([A-Za-z_][A-Za-z0-9_]*)/.exec(src.slice(i));
|
|
1856
|
+
if (m === null) return null;
|
|
1857
|
+
i += (m[1] ?? "").length;
|
|
1858
|
+
skip();
|
|
1859
|
+
let col = ident(m[1] ?? "");
|
|
1860
|
+
let excluded = false;
|
|
1861
|
+
if (src[i] === ".") {
|
|
1862
|
+
const m2 = /^\.([A-Za-z_][A-Za-z0-9_]*)/.exec(src.slice(i));
|
|
1863
|
+
if (m2 !== null) {
|
|
1864
|
+
i += (m2[0] ?? "").length;
|
|
1865
|
+
if (col.toLowerCase() === "excluded") excluded = true;
|
|
1866
|
+
col = ident(m2[1] ?? "");
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return { col, excluded };
|
|
1870
|
+
};
|
|
1871
|
+
const parseFactor = () => {
|
|
1872
|
+
const ch = peek();
|
|
1873
|
+
if (ch === "(") {
|
|
1874
|
+
i += 1;
|
|
1875
|
+
const v = parseExpr();
|
|
1876
|
+
skip();
|
|
1877
|
+
if (src[i] === ")") i += 1;
|
|
1878
|
+
return v;
|
|
1879
|
+
}
|
|
1880
|
+
const numMatch = /^(\d+(?:\.\d+)?)/.exec(src.slice(i));
|
|
1881
|
+
if (numMatch !== null) {
|
|
1882
|
+
i += (numMatch[1] ?? "").length;
|
|
1883
|
+
return Number(numMatch[1]);
|
|
1884
|
+
}
|
|
1885
|
+
const id = parseIdent();
|
|
1886
|
+
if (id !== null) {
|
|
1887
|
+
const v = id.excluded ? row[id.col] : existing[id.col];
|
|
1888
|
+
return num(v);
|
|
1889
|
+
}
|
|
1890
|
+
return 0;
|
|
1891
|
+
};
|
|
1892
|
+
const parseTerm = () => {
|
|
1893
|
+
let v = parseFactor();
|
|
1894
|
+
for (; ; ) {
|
|
1895
|
+
const ch = peek();
|
|
1896
|
+
if (ch !== "*" && ch !== "/") return v;
|
|
1897
|
+
i += 1;
|
|
1898
|
+
const rhs = parseFactor();
|
|
1899
|
+
v = ch === "*" ? v * rhs : rhs === 0 ? v : v / rhs;
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
const parseExpr = () => {
|
|
1903
|
+
let v = parseTerm();
|
|
1904
|
+
for (; ; ) {
|
|
1905
|
+
const ch = peek();
|
|
1906
|
+
if (ch !== "+" && ch !== "-") return v;
|
|
1907
|
+
i += 1;
|
|
1908
|
+
const rhs = parseTerm();
|
|
1909
|
+
v = ch === "+" ? v + rhs : v - rhs;
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
return parseExpr();
|
|
1913
|
+
}
|
|
1914
|
+
function splitInsertTuples(body) {
|
|
1915
|
+
const tuples = [];
|
|
1916
|
+
let i = 0;
|
|
1917
|
+
const n = body.length;
|
|
1918
|
+
while (i < n) {
|
|
1919
|
+
while (i < n && (body[i] === " " || body[i] === " " || body[i] === "\n" || body[i] === "\r" || body[i] === ",")) {
|
|
1920
|
+
i += 1;
|
|
1921
|
+
}
|
|
1922
|
+
if (i >= n) break;
|
|
1923
|
+
if (body[i] !== "(") {
|
|
1924
|
+
return { tuples, conflict: body.slice(i).trim() };
|
|
1925
|
+
}
|
|
1926
|
+
let depth = 0;
|
|
1927
|
+
let j = i;
|
|
1928
|
+
for (; j < n; j += 1) {
|
|
1929
|
+
if (body[j] === "(") depth += 1;
|
|
1930
|
+
else if (body[j] === ")") {
|
|
1931
|
+
depth -= 1;
|
|
1932
|
+
if (depth === 0) break;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
if (j >= n) return { tuples, conflict: null };
|
|
1936
|
+
tuples.push(body.slice(i + 1, j));
|
|
1937
|
+
i = j + 1;
|
|
1938
|
+
}
|
|
1939
|
+
return { tuples, conflict: null };
|
|
1940
|
+
}
|
|
1941
|
+
function parseConflictClause(clause, cursor, fallbackCols) {
|
|
1942
|
+
const m = /^ON\s+CONFLICT(?:\s*\(([^)]*)\))?\s+DO\s+(UPDATE\s+SET\s+([\s\S]+)|NOTHING\s*)$/i.exec(
|
|
1943
|
+
clause.trim()
|
|
1944
|
+
);
|
|
1945
|
+
if (m === null) {
|
|
1946
|
+
throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 ON CONFLICT \u5B50\u53E5\uFF1A${clause.trim().slice(0, 60)}\u2026`);
|
|
1947
|
+
}
|
|
1948
|
+
const colsRaw = m[1];
|
|
1949
|
+
const cols = colsRaw === void 0 ? fallbackCols : colsRaw.split(",").map(ident);
|
|
1950
|
+
if (cols.length === 0) throw new SimDbError("DB_UNSAFE_OP", "ON CONFLICT \u51B2\u7A81\u5217\u4E3A\u7A7A");
|
|
1951
|
+
const action = m[2] ?? "";
|
|
1952
|
+
if (/^NOTHING/i.test(action)) return { cols, sets: [] };
|
|
1953
|
+
const setRaw = m[3] ?? "";
|
|
1954
|
+
const sets = splitListItems(setRaw).map((part) => {
|
|
1955
|
+
const eq = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]+)$/i.exec(part.trim());
|
|
1956
|
+
if (eq === null) {
|
|
1957
|
+
throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 SET \u8D4B\u503C\uFF1A${part.trim().slice(0, 60)}\u2026`);
|
|
1958
|
+
}
|
|
1959
|
+
const col = ident(eq[1] ?? "");
|
|
1960
|
+
const rhs = (eq[2] ?? "").trim();
|
|
1961
|
+
let value;
|
|
1962
|
+
if (rhs === "?") {
|
|
1963
|
+
value = cursor.take();
|
|
1964
|
+
} else {
|
|
1965
|
+
const ex = /^excluded\.([A-Za-z_][A-Za-z0-9_]*)$/i.exec(rhs);
|
|
1966
|
+
if (ex !== null) {
|
|
1967
|
+
value = new ExcludedRef(ident(ex[1] ?? ""));
|
|
1968
|
+
} else if (/^([A-Za-z_][A-Za-z0-9_]*)$/.test(rhs) && !/^NULL$/i.test(rhs)) {
|
|
1969
|
+
value = new ColumnRef(ident(rhs));
|
|
1970
|
+
} else if (/^'.*'$/.test(rhs) || /^".*"$/.test(rhs) || /^NULL$/i.test(rhs)) {
|
|
1971
|
+
value = stripQuotes(rhs);
|
|
1972
|
+
} else {
|
|
1973
|
+
value = new ExprRef(rhs);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
return { col, value };
|
|
1977
|
+
});
|
|
1978
|
+
return { cols, sets };
|
|
1979
|
+
}
|
|
1773
1980
|
function matchValue(value, clause) {
|
|
1774
1981
|
const op = clause.operator;
|
|
1775
1982
|
if (op === "IS NULL") return value === null || value === void 0;
|
|
@@ -1819,6 +2026,15 @@ function indexAfter(start, matches, tail) {
|
|
|
1819
2026
|
const end = candidates.length === 0 ? -1 : Math.min(...candidates);
|
|
1820
2027
|
return end === -1 ? tail.length : end;
|
|
1821
2028
|
}
|
|
2029
|
+
function parseSelectItem(raw) {
|
|
2030
|
+
const t = raw.trim();
|
|
2031
|
+
const as = /\s+AS\s+([A-Za-z_][A-Za-z0-9_]*)$/i.exec(t);
|
|
2032
|
+
let name = as === null ? t : t.slice(0, as.index).trim();
|
|
2033
|
+
if (unqualifyColumn(name) === "*") return { name: "", qualified: "", alias: "", star: true };
|
|
2034
|
+
const qualified = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? ident(name) : "";
|
|
2035
|
+
const plain = unqualifyColumn(ident(name));
|
|
2036
|
+
return { name: plain, qualified, alias: as === null ? plain : ident(as[1] ?? ""), star: false };
|
|
2037
|
+
}
|
|
1822
2038
|
function project(rows, selectRaw) {
|
|
1823
2039
|
if (/^count\s*\(\s*\*/i.test(selectRaw) || /^count\s*\(\s*1\)/i.test(selectRaw)) {
|
|
1824
2040
|
const alias = selectRaw.match(/\bAS\s+([A-Za-z_][A-Za-z0-9_]*)/i)?.[1] ?? "n";
|
|
@@ -1829,14 +2045,25 @@ function project(rows, selectRaw) {
|
|
|
1829
2045
|
return [{ [constMatch[2]]: Number(constMatch[1]) }];
|
|
1830
2046
|
}
|
|
1831
2047
|
if (selectRaw.trim() === "*") return rows;
|
|
1832
|
-
const
|
|
2048
|
+
const items = selectRaw.split(",").map(parseSelectItem);
|
|
2049
|
+
const valueOf = (row, item) => item.qualified !== "" && row[item.qualified] !== void 0 ? row[item.qualified] : row[item.name];
|
|
2050
|
+
if (items.some((item) => item.star)) {
|
|
2051
|
+
return rows.map((row) => {
|
|
2052
|
+
const out = { ...row };
|
|
2053
|
+
for (const item of items) {
|
|
2054
|
+
if (item.star) continue;
|
|
2055
|
+
out[item.alias] = valueOf(row, item);
|
|
2056
|
+
}
|
|
2057
|
+
return out;
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
1833
2060
|
return rows.map((row) => {
|
|
1834
2061
|
const out = {};
|
|
1835
|
-
for (const
|
|
2062
|
+
for (const item of items) out[item.alias] = valueOf(row, item);
|
|
1836
2063
|
return out;
|
|
1837
2064
|
});
|
|
1838
2065
|
}
|
|
1839
|
-
var SimDbError, ParamCursor, SimSqlEngine;
|
|
2066
|
+
var SimDbError, ParamCursor, ExcludedRef, ColumnRef, ExprRef, SimSqlEngine;
|
|
1840
2067
|
var init_sql_engine = __esm({
|
|
1841
2068
|
"packages/runtime/src/sim/sql-engine.ts"() {
|
|
1842
2069
|
"use strict";
|
|
@@ -1858,6 +2085,21 @@ var init_sql_engine = __esm({
|
|
|
1858
2085
|
return v ?? null;
|
|
1859
2086
|
}
|
|
1860
2087
|
};
|
|
2088
|
+
ExcludedRef = class {
|
|
2089
|
+
constructor(column) {
|
|
2090
|
+
this.column = column;
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
2093
|
+
ColumnRef = class {
|
|
2094
|
+
constructor(column) {
|
|
2095
|
+
this.column = column;
|
|
2096
|
+
}
|
|
2097
|
+
};
|
|
2098
|
+
ExprRef = class {
|
|
2099
|
+
constructor(source) {
|
|
2100
|
+
this.source = source;
|
|
2101
|
+
}
|
|
2102
|
+
};
|
|
1861
2103
|
SimSqlEngine = class {
|
|
1862
2104
|
tables = {};
|
|
1863
2105
|
storage;
|
|
@@ -1967,16 +2209,36 @@ var init_sql_engine = __esm({
|
|
|
1967
2209
|
const valueBody = match[4].trim();
|
|
1968
2210
|
const cursor = new ParamCursor(params);
|
|
1969
2211
|
const table = this.ensureTable(tableName);
|
|
2212
|
+
const { tuples, conflict } = splitInsertTuples(valueBody);
|
|
2213
|
+
if (tuples.length === 0) throw new SimDbError("DB_UNSAFE_OP", "INSERT \u7F3A\u5C11 VALUES \u5143\u7EC4");
|
|
2214
|
+
const conflictSpec = conflict === null ? null : parseConflictClause(
|
|
2215
|
+
conflict,
|
|
2216
|
+
cursor,
|
|
2217
|
+
table.columns.filter((c) => c.primaryKey).map((c) => c.name)
|
|
2218
|
+
);
|
|
1970
2219
|
let inserted = 0;
|
|
1971
|
-
for (const
|
|
1972
|
-
const
|
|
1973
|
-
const values = splitListItems(inner).map(
|
|
2220
|
+
for (const tuple of tuples) {
|
|
2221
|
+
const values = splitListItems(tuple).map(
|
|
1974
2222
|
(item) => item.trim() === "?" ? cursor.take() : stripQuotes(item)
|
|
1975
2223
|
);
|
|
1976
2224
|
const row = {};
|
|
1977
2225
|
cols.forEach((col, i) => {
|
|
1978
2226
|
row[col] = values[i] ?? null;
|
|
1979
2227
|
});
|
|
2228
|
+
if (conflictSpec !== null) {
|
|
2229
|
+
const existing = table.rows.find(
|
|
2230
|
+
(r) => conflictSpec.cols.every((c) => r[c] === row[c])
|
|
2231
|
+
);
|
|
2232
|
+
if (existing !== void 0) {
|
|
2233
|
+
const oldRow = { ...existing };
|
|
2234
|
+
for (const set of conflictSpec.sets) {
|
|
2235
|
+
existing[set.col] = resolveSetValue(set.value, oldRow, row);
|
|
2236
|
+
}
|
|
2237
|
+
inserted += 1;
|
|
2238
|
+
void this.persist();
|
|
2239
|
+
continue;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
1980
2242
|
this.applyAutoincrement(table, row);
|
|
1981
2243
|
table.rows.push(row);
|
|
1982
2244
|
inserted += 1;
|
|
@@ -2045,6 +2307,20 @@ var init_sql_engine = __esm({
|
|
|
2045
2307
|
return project(this.catalog(), selectRaw);
|
|
2046
2308
|
}
|
|
2047
2309
|
const table = this.ensureTable(tableName);
|
|
2310
|
+
let rows = table.rows;
|
|
2311
|
+
const joins = parseLeftJoins(tail);
|
|
2312
|
+
if (joins.length > 0) {
|
|
2313
|
+
rows = table.rows.map((row) => {
|
|
2314
|
+
const merged = { ...row };
|
|
2315
|
+
for (const j of joins) {
|
|
2316
|
+
const joined = this.tables[j.table]?.rows.find((jr) => jr[j.leftCol] === merged[j.rightCol]);
|
|
2317
|
+
if (joined !== void 0) {
|
|
2318
|
+
for (const [k, v] of Object.entries(joined)) merged[`${j.alias}.${k}`] = v;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
return merged;
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2048
2324
|
const whereMatch = /\bWHERE\b/i.exec(tail);
|
|
2049
2325
|
const orderMatch = /\bORDER\s+BY\b/i.exec(tail);
|
|
2050
2326
|
const limitMatch = /\bLIMIT\b/i.exec(tail);
|
|
@@ -2053,7 +2329,6 @@ var init_sql_engine = __esm({
|
|
|
2053
2329
|
whereMatch.index + whereMatch[0].length,
|
|
2054
2330
|
indexAfter(whereMatch.index, [orderMatch, limitMatch, offsetMatch], tail)
|
|
2055
2331
|
);
|
|
2056
|
-
let rows = table.rows;
|
|
2057
2332
|
if (whereRaw.trim().length > 0) {
|
|
2058
2333
|
const clauses = parseWhereClauses(whereRaw, cursor);
|
|
2059
2334
|
rows = rows.filter((row) => matchWhere(clauses, row));
|
|
@@ -2063,9 +2338,9 @@ var init_sql_engine = __esm({
|
|
|
2063
2338
|
orderMatch.index + orderMatch[0].length,
|
|
2064
2339
|
indexAfter(orderMatch.index, [limitMatch, offsetMatch], tail)
|
|
2065
2340
|
);
|
|
2066
|
-
const oc = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+(asc|desc)/i.exec(orderRaw);
|
|
2341
|
+
const oc = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_.]*))\s+(asc|desc)/i.exec(orderRaw);
|
|
2067
2342
|
if (oc !== null) {
|
|
2068
|
-
const col = ident(oc[1]);
|
|
2343
|
+
const col = unqualifyColumn(ident(oc[1]));
|
|
2069
2344
|
const dir = oc[3].toLowerCase();
|
|
2070
2345
|
rows = [...rows].toSorted((a, b) => {
|
|
2071
2346
|
const av = a[col];
|
|
@@ -4364,6 +4639,7 @@ function createBearerUserResolver(issuer) {
|
|
|
4364
4639
|
}
|
|
4365
4640
|
async function collectFunctions(dir) {
|
|
4366
4641
|
const files = {};
|
|
4642
|
+
const isRuntimeSource = (name) => (name.endsWith(".ts") || name.endsWith(".js")) && !name.endsWith(".d.ts") && !name.endsWith(".test.ts");
|
|
4367
4643
|
const walk = async (sub, prefix) => {
|
|
4368
4644
|
let entries;
|
|
4369
4645
|
try {
|
|
@@ -4376,7 +4652,7 @@ async function collectFunctions(dir) {
|
|
|
4376
4652
|
const full = join10(sub, entry.name);
|
|
4377
4653
|
if (entry.isDirectory()) {
|
|
4378
4654
|
await walk(full, rel);
|
|
4379
|
-
} else if (entry.isFile() && entry.name
|
|
4655
|
+
} else if (entry.isFile() && isRuntimeSource(entry.name)) {
|
|
4380
4656
|
files[rel] = await readFile7(full, "utf8");
|
|
4381
4657
|
}
|
|
4382
4658
|
}
|
|
@@ -4983,22 +5259,29 @@ function splitSqlStatements(source) {
|
|
|
4983
5259
|
let inQuote = false;
|
|
4984
5260
|
const lines = source.split("\n");
|
|
4985
5261
|
for (const rawLine of lines) {
|
|
4986
|
-
const
|
|
4987
|
-
if (
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
5262
|
+
const trimmed = rawLine.trim();
|
|
5263
|
+
if (trimmed.length === 0 || trimmed.startsWith("--")) continue;
|
|
5264
|
+
for (let i = 0; i < rawLine.length; i++) {
|
|
5265
|
+
const ch = rawLine[i];
|
|
5266
|
+
if (inQuote) {
|
|
5267
|
+
if (ch === "'") {
|
|
5268
|
+
if (rawLine[i + 1] === "'") {
|
|
5269
|
+
buffer += ch + rawLine[i + 1];
|
|
5270
|
+
i++;
|
|
5271
|
+
continue;
|
|
5272
|
+
}
|
|
5273
|
+
inQuote = false;
|
|
4996
5274
|
}
|
|
4997
|
-
inQuote = !inQuote;
|
|
4998
5275
|
buffer += ch;
|
|
4999
5276
|
continue;
|
|
5000
5277
|
}
|
|
5001
|
-
if (ch === "
|
|
5278
|
+
if (ch === "'") {
|
|
5279
|
+
inQuote = true;
|
|
5280
|
+
buffer += ch;
|
|
5281
|
+
continue;
|
|
5282
|
+
}
|
|
5283
|
+
if (ch === "-" && rawLine[i + 1] === "-") break;
|
|
5284
|
+
if (ch === ";") {
|
|
5002
5285
|
const statement = buffer.trim();
|
|
5003
5286
|
if (statement.length > 0) statements.push(statement);
|
|
5004
5287
|
buffer = "";
|
|
@@ -5532,7 +5815,8 @@ async function collectShared(functionsDir) {
|
|
|
5532
5815
|
await walk(join13(dir, entry.name));
|
|
5533
5816
|
continue;
|
|
5534
5817
|
}
|
|
5535
|
-
|
|
5818
|
+
const isRuntimeSource = (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".test.ts");
|
|
5819
|
+
if (!isRuntimeSource) continue;
|
|
5536
5820
|
const full = join13(dir, entry.name);
|
|
5537
5821
|
const rel = relative3(sharedDir, full).split(sep).join("/");
|
|
5538
5822
|
files[`_shared/${rel}`] = await readFile11(full, "utf8");
|
|
@@ -5643,7 +5927,7 @@ var init_frontend_draft = __esm({
|
|
|
5643
5927
|
"shared/frontend-draft.ts"() {
|
|
5644
5928
|
"use strict";
|
|
5645
5929
|
FRONTEND_DRAFT_FILE_PATH_PATTERN = /^[a-zA-Z0-9._][a-zA-Z0-9._/-]*$/;
|
|
5646
|
-
FRONTEND_DRAFT_MAX_TOTAL_SOURCE_BYTES =
|
|
5930
|
+
FRONTEND_DRAFT_MAX_TOTAL_SOURCE_BYTES = 512 * 1024;
|
|
5647
5931
|
FRONTEND_DRAFT_MAX_FILES = 100;
|
|
5648
5932
|
}
|
|
5649
5933
|
});
|
package/dist/vite.js
CHANGED
|
@@ -1138,6 +1138,7 @@ function stripQuotes(raw) {
|
|
|
1138
1138
|
const t = raw.trim();
|
|
1139
1139
|
if (/^'.*'$/.test(t)) return t.slice(1, -1);
|
|
1140
1140
|
if (/^".*"$/.test(t)) return t.slice(1, -1);
|
|
1141
|
+
if (/^NULL$/i.test(t)) return null;
|
|
1141
1142
|
return t;
|
|
1142
1143
|
}
|
|
1143
1144
|
function parseCreateTable(ddl) {
|
|
@@ -1158,50 +1159,109 @@ function parseCreateTable(ddl) {
|
|
|
1158
1159
|
}
|
|
1159
1160
|
return { name, columns };
|
|
1160
1161
|
}
|
|
1161
|
-
function
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
const inner = rhs.replace(/^\(|\)$/g, "");
|
|
1185
|
-
const items = splitListItems(inner);
|
|
1186
|
-
const list = items.map((item) => {
|
|
1187
|
-
if (item.trim() === "?") return cursor.take();
|
|
1188
|
-
return stripQuotes(item);
|
|
1189
|
-
});
|
|
1190
|
-
clauses.push({ column: col, operator: op, value: list, list: true });
|
|
1162
|
+
function splitTopLevel(raw, token) {
|
|
1163
|
+
const parts = [];
|
|
1164
|
+
let depth = 0;
|
|
1165
|
+
let inQuote = false;
|
|
1166
|
+
let buffer = "";
|
|
1167
|
+
const n = raw.length;
|
|
1168
|
+
const isTokenAt = (i) => {
|
|
1169
|
+
const word = raw.slice(i, i + token.length);
|
|
1170
|
+
if (word.toUpperCase() !== token) return false;
|
|
1171
|
+
const before = i === 0 ? "" : raw[i - 1] ?? "";
|
|
1172
|
+
const after = raw[i + token.length] ?? "";
|
|
1173
|
+
return !/[A-Za-z0-9_.]/.test(before) && !/[A-Za-z0-9_.]/.test(after);
|
|
1174
|
+
};
|
|
1175
|
+
for (let i = 0; i < n; i += 1) {
|
|
1176
|
+
const ch = raw[i] ?? "";
|
|
1177
|
+
if (ch === "'") {
|
|
1178
|
+
if (inQuote && raw[i + 1] === "'") {
|
|
1179
|
+
buffer += ch + (raw[i + 1] ?? "");
|
|
1180
|
+
i += 1;
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
inQuote = !inQuote;
|
|
1184
|
+
buffer += ch;
|
|
1191
1185
|
continue;
|
|
1192
1186
|
}
|
|
1193
|
-
|
|
1194
|
-
if (
|
|
1195
|
-
|
|
1187
|
+
if (!inQuote && ch === "(") depth += 1;
|
|
1188
|
+
else if (!inQuote && ch === ")") depth -= 1;
|
|
1189
|
+
if (!inQuote && depth === 0 && isTokenAt(i)) {
|
|
1190
|
+
parts.push(buffer);
|
|
1191
|
+
buffer = "";
|
|
1192
|
+
i += token.length - 1;
|
|
1196
1193
|
} else {
|
|
1197
|
-
|
|
1194
|
+
buffer += ch;
|
|
1198
1195
|
}
|
|
1199
|
-
clauses.push({ column: col, operator: op, value, list: false });
|
|
1200
1196
|
}
|
|
1201
|
-
|
|
1197
|
+
parts.push(buffer);
|
|
1198
|
+
return parts;
|
|
1199
|
+
}
|
|
1200
|
+
function unqualifyColumn(raw) {
|
|
1201
|
+
const t = raw.trim();
|
|
1202
|
+
const dot = t.lastIndexOf(".");
|
|
1203
|
+
if (dot > 0 && /^[A-Za-z_][A-Za-z0-9_]*\./.test(t)) return t.slice(dot + 1);
|
|
1204
|
+
return t;
|
|
1205
|
+
}
|
|
1206
|
+
function parseLeftJoins(tail) {
|
|
1207
|
+
const joins = [];
|
|
1208
|
+
const re = /LEFT\s+JOIN\s+((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+([A-Za-z_][A-Za-z0-9_]*)\s+ON\s+([A-Za-z_][A-Za-z0-9_.]*)\s*=\s*([A-Za-z_][A-Za-z0-9_.]*)/gi;
|
|
1209
|
+
let m;
|
|
1210
|
+
while ((m = re.exec(tail)) !== null) {
|
|
1211
|
+
joins.push({
|
|
1212
|
+
table: ident(m[1] ?? ""),
|
|
1213
|
+
alias: m[3] ?? "",
|
|
1214
|
+
leftCol: unqualifyColumn(m[4] ?? ""),
|
|
1215
|
+
rightCol: unqualifyColumn(m[5] ?? "")
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
return joins;
|
|
1202
1219
|
}
|
|
1203
|
-
function
|
|
1204
|
-
|
|
1220
|
+
function parseWhereClauses(whereRaw, cursor) {
|
|
1221
|
+
const parseAndGroup = (andRaw) => {
|
|
1222
|
+
const clauses = [];
|
|
1223
|
+
for (const part of splitTopLevel(andRaw, "AND")) {
|
|
1224
|
+
const t = part.trim();
|
|
1225
|
+
if (t.length === 0) continue;
|
|
1226
|
+
if (/\bIS\s+NULL\b/i.test(t)) {
|
|
1227
|
+
clauses.push({ column: unqualifyColumn(ident(t.split(/\s+IS\s+NULL\b/i)[0] ?? "")), operator: "IS NULL", value: null, list: false });
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1230
|
+
if (/\bIS\s+NOT\s+NULL\b/i.test(t)) {
|
|
1231
|
+
clauses.push({ column: unqualifyColumn(ident(t.split(/\s+IS\s+NOT\s+NULL\b/i)[0] ?? "")), operator: "IS NOT NULL", value: null, list: false });
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
const opMatch = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_.]*))\s*(=|!=|>=|<=|>|<|like|in|not\s+in)\s*(.+)$/i.exec(
|
|
1235
|
+
t
|
|
1236
|
+
);
|
|
1237
|
+
if (opMatch === null) continue;
|
|
1238
|
+
const col = unqualifyColumn(ident(opMatch[1] ?? ""));
|
|
1239
|
+
const op = (opMatch[3] ?? opMatch[2] ?? "").toLowerCase();
|
|
1240
|
+
const rhs = (opMatch[4] ?? "").trim();
|
|
1241
|
+
if (op === "in" || op === "not in") {
|
|
1242
|
+
const inner = rhs.replace(/^\(|\)$/g, "");
|
|
1243
|
+
const items = splitListItems(inner);
|
|
1244
|
+
const list = items.map((item) => {
|
|
1245
|
+
if (item.trim() === "?") return cursor.take();
|
|
1246
|
+
return stripQuotes(item);
|
|
1247
|
+
});
|
|
1248
|
+
clauses.push({ column: col, operator: op, value: list, list: true });
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1251
|
+
let value;
|
|
1252
|
+
if (rhs === "?") {
|
|
1253
|
+
value = cursor.take();
|
|
1254
|
+
} else {
|
|
1255
|
+
value = stripQuotes(rhs);
|
|
1256
|
+
}
|
|
1257
|
+
clauses.push({ column: col, operator: op, value, list: false });
|
|
1258
|
+
}
|
|
1259
|
+
return clauses;
|
|
1260
|
+
};
|
|
1261
|
+
return splitTopLevel(whereRaw, "OR").map((group) => group.trim()).filter((group) => group.length > 0).map(parseAndGroup);
|
|
1262
|
+
}
|
|
1263
|
+
function matchWhere(groups, row) {
|
|
1264
|
+
return groups.some((clauses) => clauses.every((c) => matchValue(row[c.column], c)));
|
|
1205
1265
|
}
|
|
1206
1266
|
function splitListItems(inner) {
|
|
1207
1267
|
const items = [];
|
|
@@ -1221,6 +1281,168 @@ function splitListItems(inner) {
|
|
|
1221
1281
|
items.push(buffer);
|
|
1222
1282
|
return items;
|
|
1223
1283
|
}
|
|
1284
|
+
var ExcludedRef = class {
|
|
1285
|
+
constructor(column) {
|
|
1286
|
+
this.column = column;
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
var ColumnRef = class {
|
|
1290
|
+
constructor(column) {
|
|
1291
|
+
this.column = column;
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
var ExprRef = class {
|
|
1295
|
+
constructor(source) {
|
|
1296
|
+
this.source = source;
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
function resolveSetValue(value, existing, row) {
|
|
1300
|
+
if (value instanceof ExcludedRef) return row[value.column];
|
|
1301
|
+
if (value instanceof ColumnRef) return existing[value.column];
|
|
1302
|
+
if (value instanceof ExprRef) return evalArithmetic(value.source, existing, row);
|
|
1303
|
+
return value;
|
|
1304
|
+
}
|
|
1305
|
+
function evalArithmetic(src, existing, row) {
|
|
1306
|
+
const num = (v) => {
|
|
1307
|
+
const n2 = Number(v);
|
|
1308
|
+
return Number.isFinite(n2) ? n2 : 0;
|
|
1309
|
+
};
|
|
1310
|
+
let i = 0;
|
|
1311
|
+
const n = src.length;
|
|
1312
|
+
const skip = () => {
|
|
1313
|
+
while (i < n && /\s/.test(src[i] ?? "")) i += 1;
|
|
1314
|
+
};
|
|
1315
|
+
const peek = () => {
|
|
1316
|
+
skip();
|
|
1317
|
+
return i < n ? src[i] ?? "" : null;
|
|
1318
|
+
};
|
|
1319
|
+
const parseIdent = () => {
|
|
1320
|
+
skip();
|
|
1321
|
+
const m = /^([A-Za-z_][A-Za-z0-9_]*)/.exec(src.slice(i));
|
|
1322
|
+
if (m === null) return null;
|
|
1323
|
+
i += (m[1] ?? "").length;
|
|
1324
|
+
skip();
|
|
1325
|
+
let col = ident(m[1] ?? "");
|
|
1326
|
+
let excluded = false;
|
|
1327
|
+
if (src[i] === ".") {
|
|
1328
|
+
const m2 = /^\.([A-Za-z_][A-Za-z0-9_]*)/.exec(src.slice(i));
|
|
1329
|
+
if (m2 !== null) {
|
|
1330
|
+
i += (m2[0] ?? "").length;
|
|
1331
|
+
if (col.toLowerCase() === "excluded") excluded = true;
|
|
1332
|
+
col = ident(m2[1] ?? "");
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return { col, excluded };
|
|
1336
|
+
};
|
|
1337
|
+
const parseFactor = () => {
|
|
1338
|
+
const ch = peek();
|
|
1339
|
+
if (ch === "(") {
|
|
1340
|
+
i += 1;
|
|
1341
|
+
const v = parseExpr();
|
|
1342
|
+
skip();
|
|
1343
|
+
if (src[i] === ")") i += 1;
|
|
1344
|
+
return v;
|
|
1345
|
+
}
|
|
1346
|
+
const numMatch = /^(\d+(?:\.\d+)?)/.exec(src.slice(i));
|
|
1347
|
+
if (numMatch !== null) {
|
|
1348
|
+
i += (numMatch[1] ?? "").length;
|
|
1349
|
+
return Number(numMatch[1]);
|
|
1350
|
+
}
|
|
1351
|
+
const id = parseIdent();
|
|
1352
|
+
if (id !== null) {
|
|
1353
|
+
const v = id.excluded ? row[id.col] : existing[id.col];
|
|
1354
|
+
return num(v);
|
|
1355
|
+
}
|
|
1356
|
+
return 0;
|
|
1357
|
+
};
|
|
1358
|
+
const parseTerm = () => {
|
|
1359
|
+
let v = parseFactor();
|
|
1360
|
+
for (; ; ) {
|
|
1361
|
+
const ch = peek();
|
|
1362
|
+
if (ch !== "*" && ch !== "/") return v;
|
|
1363
|
+
i += 1;
|
|
1364
|
+
const rhs = parseFactor();
|
|
1365
|
+
v = ch === "*" ? v * rhs : rhs === 0 ? v : v / rhs;
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
const parseExpr = () => {
|
|
1369
|
+
let v = parseTerm();
|
|
1370
|
+
for (; ; ) {
|
|
1371
|
+
const ch = peek();
|
|
1372
|
+
if (ch !== "+" && ch !== "-") return v;
|
|
1373
|
+
i += 1;
|
|
1374
|
+
const rhs = parseTerm();
|
|
1375
|
+
v = ch === "+" ? v + rhs : v - rhs;
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
return parseExpr();
|
|
1379
|
+
}
|
|
1380
|
+
function splitInsertTuples(body) {
|
|
1381
|
+
const tuples = [];
|
|
1382
|
+
let i = 0;
|
|
1383
|
+
const n = body.length;
|
|
1384
|
+
while (i < n) {
|
|
1385
|
+
while (i < n && (body[i] === " " || body[i] === " " || body[i] === "\n" || body[i] === "\r" || body[i] === ",")) {
|
|
1386
|
+
i += 1;
|
|
1387
|
+
}
|
|
1388
|
+
if (i >= n) break;
|
|
1389
|
+
if (body[i] !== "(") {
|
|
1390
|
+
return { tuples, conflict: body.slice(i).trim() };
|
|
1391
|
+
}
|
|
1392
|
+
let depth = 0;
|
|
1393
|
+
let j = i;
|
|
1394
|
+
for (; j < n; j += 1) {
|
|
1395
|
+
if (body[j] === "(") depth += 1;
|
|
1396
|
+
else if (body[j] === ")") {
|
|
1397
|
+
depth -= 1;
|
|
1398
|
+
if (depth === 0) break;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
if (j >= n) return { tuples, conflict: null };
|
|
1402
|
+
tuples.push(body.slice(i + 1, j));
|
|
1403
|
+
i = j + 1;
|
|
1404
|
+
}
|
|
1405
|
+
return { tuples, conflict: null };
|
|
1406
|
+
}
|
|
1407
|
+
function parseConflictClause(clause, cursor, fallbackCols) {
|
|
1408
|
+
const m = /^ON\s+CONFLICT(?:\s*\(([^)]*)\))?\s+DO\s+(UPDATE\s+SET\s+([\s\S]+)|NOTHING\s*)$/i.exec(
|
|
1409
|
+
clause.trim()
|
|
1410
|
+
);
|
|
1411
|
+
if (m === null) {
|
|
1412
|
+
throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 ON CONFLICT \u5B50\u53E5\uFF1A${clause.trim().slice(0, 60)}\u2026`);
|
|
1413
|
+
}
|
|
1414
|
+
const colsRaw = m[1];
|
|
1415
|
+
const cols = colsRaw === void 0 ? fallbackCols : colsRaw.split(",").map(ident);
|
|
1416
|
+
if (cols.length === 0) throw new SimDbError("DB_UNSAFE_OP", "ON CONFLICT \u51B2\u7A81\u5217\u4E3A\u7A7A");
|
|
1417
|
+
const action = m[2] ?? "";
|
|
1418
|
+
if (/^NOTHING/i.test(action)) return { cols, sets: [] };
|
|
1419
|
+
const setRaw = m[3] ?? "";
|
|
1420
|
+
const sets = splitListItems(setRaw).map((part) => {
|
|
1421
|
+
const eq = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]+)$/i.exec(part.trim());
|
|
1422
|
+
if (eq === null) {
|
|
1423
|
+
throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 SET \u8D4B\u503C\uFF1A${part.trim().slice(0, 60)}\u2026`);
|
|
1424
|
+
}
|
|
1425
|
+
const col = ident(eq[1] ?? "");
|
|
1426
|
+
const rhs = (eq[2] ?? "").trim();
|
|
1427
|
+
let value;
|
|
1428
|
+
if (rhs === "?") {
|
|
1429
|
+
value = cursor.take();
|
|
1430
|
+
} else {
|
|
1431
|
+
const ex = /^excluded\.([A-Za-z_][A-Za-z0-9_]*)$/i.exec(rhs);
|
|
1432
|
+
if (ex !== null) {
|
|
1433
|
+
value = new ExcludedRef(ident(ex[1] ?? ""));
|
|
1434
|
+
} else if (/^([A-Za-z_][A-Za-z0-9_]*)$/.test(rhs) && !/^NULL$/i.test(rhs)) {
|
|
1435
|
+
value = new ColumnRef(ident(rhs));
|
|
1436
|
+
} else if (/^'.*'$/.test(rhs) || /^".*"$/.test(rhs) || /^NULL$/i.test(rhs)) {
|
|
1437
|
+
value = stripQuotes(rhs);
|
|
1438
|
+
} else {
|
|
1439
|
+
value = new ExprRef(rhs);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return { col, value };
|
|
1443
|
+
});
|
|
1444
|
+
return { cols, sets };
|
|
1445
|
+
}
|
|
1224
1446
|
function matchValue(value, clause) {
|
|
1225
1447
|
const op = clause.operator;
|
|
1226
1448
|
if (op === "IS NULL") return value === null || value === void 0;
|
|
@@ -1374,16 +1596,36 @@ var SimSqlEngine = class {
|
|
|
1374
1596
|
const valueBody = match[4].trim();
|
|
1375
1597
|
const cursor = new ParamCursor(params);
|
|
1376
1598
|
const table = this.ensureTable(tableName);
|
|
1599
|
+
const { tuples, conflict } = splitInsertTuples(valueBody);
|
|
1600
|
+
if (tuples.length === 0) throw new SimDbError("DB_UNSAFE_OP", "INSERT \u7F3A\u5C11 VALUES \u5143\u7EC4");
|
|
1601
|
+
const conflictSpec = conflict === null ? null : parseConflictClause(
|
|
1602
|
+
conflict,
|
|
1603
|
+
cursor,
|
|
1604
|
+
table.columns.filter((c) => c.primaryKey).map((c) => c.name)
|
|
1605
|
+
);
|
|
1377
1606
|
let inserted = 0;
|
|
1378
|
-
for (const
|
|
1379
|
-
const
|
|
1380
|
-
const values = splitListItems(inner).map(
|
|
1607
|
+
for (const tuple of tuples) {
|
|
1608
|
+
const values = splitListItems(tuple).map(
|
|
1381
1609
|
(item) => item.trim() === "?" ? cursor.take() : stripQuotes(item)
|
|
1382
1610
|
);
|
|
1383
1611
|
const row = {};
|
|
1384
1612
|
cols.forEach((col, i) => {
|
|
1385
1613
|
row[col] = values[i] ?? null;
|
|
1386
1614
|
});
|
|
1615
|
+
if (conflictSpec !== null) {
|
|
1616
|
+
const existing = table.rows.find(
|
|
1617
|
+
(r) => conflictSpec.cols.every((c) => r[c] === row[c])
|
|
1618
|
+
);
|
|
1619
|
+
if (existing !== void 0) {
|
|
1620
|
+
const oldRow = { ...existing };
|
|
1621
|
+
for (const set of conflictSpec.sets) {
|
|
1622
|
+
existing[set.col] = resolveSetValue(set.value, oldRow, row);
|
|
1623
|
+
}
|
|
1624
|
+
inserted += 1;
|
|
1625
|
+
void this.persist();
|
|
1626
|
+
continue;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1387
1629
|
this.applyAutoincrement(table, row);
|
|
1388
1630
|
table.rows.push(row);
|
|
1389
1631
|
inserted += 1;
|
|
@@ -1452,6 +1694,20 @@ var SimSqlEngine = class {
|
|
|
1452
1694
|
return project(this.catalog(), selectRaw);
|
|
1453
1695
|
}
|
|
1454
1696
|
const table = this.ensureTable(tableName);
|
|
1697
|
+
let rows = table.rows;
|
|
1698
|
+
const joins = parseLeftJoins(tail);
|
|
1699
|
+
if (joins.length > 0) {
|
|
1700
|
+
rows = table.rows.map((row) => {
|
|
1701
|
+
const merged = { ...row };
|
|
1702
|
+
for (const j of joins) {
|
|
1703
|
+
const joined = this.tables[j.table]?.rows.find((jr) => jr[j.leftCol] === merged[j.rightCol]);
|
|
1704
|
+
if (joined !== void 0) {
|
|
1705
|
+
for (const [k, v] of Object.entries(joined)) merged[`${j.alias}.${k}`] = v;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return merged;
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1455
1711
|
const whereMatch = /\bWHERE\b/i.exec(tail);
|
|
1456
1712
|
const orderMatch = /\bORDER\s+BY\b/i.exec(tail);
|
|
1457
1713
|
const limitMatch = /\bLIMIT\b/i.exec(tail);
|
|
@@ -1460,7 +1716,6 @@ var SimSqlEngine = class {
|
|
|
1460
1716
|
whereMatch.index + whereMatch[0].length,
|
|
1461
1717
|
indexAfter(whereMatch.index, [orderMatch, limitMatch, offsetMatch], tail)
|
|
1462
1718
|
);
|
|
1463
|
-
let rows = table.rows;
|
|
1464
1719
|
if (whereRaw.trim().length > 0) {
|
|
1465
1720
|
const clauses = parseWhereClauses(whereRaw, cursor);
|
|
1466
1721
|
rows = rows.filter((row) => matchWhere(clauses, row));
|
|
@@ -1470,9 +1725,9 @@ var SimSqlEngine = class {
|
|
|
1470
1725
|
orderMatch.index + orderMatch[0].length,
|
|
1471
1726
|
indexAfter(orderMatch.index, [limitMatch, offsetMatch], tail)
|
|
1472
1727
|
);
|
|
1473
|
-
const oc = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+(asc|desc)/i.exec(orderRaw);
|
|
1728
|
+
const oc = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_.]*))\s+(asc|desc)/i.exec(orderRaw);
|
|
1474
1729
|
if (oc !== null) {
|
|
1475
|
-
const col = ident(oc[1]);
|
|
1730
|
+
const col = unqualifyColumn(ident(oc[1]));
|
|
1476
1731
|
const dir = oc[3].toLowerCase();
|
|
1477
1732
|
rows = [...rows].toSorted((a, b) => {
|
|
1478
1733
|
const av = a[col];
|
|
@@ -1517,6 +1772,15 @@ function indexAfter(start, matches, tail) {
|
|
|
1517
1772
|
const end = candidates.length === 0 ? -1 : Math.min(...candidates);
|
|
1518
1773
|
return end === -1 ? tail.length : end;
|
|
1519
1774
|
}
|
|
1775
|
+
function parseSelectItem(raw) {
|
|
1776
|
+
const t = raw.trim();
|
|
1777
|
+
const as = /\s+AS\s+([A-Za-z_][A-Za-z0-9_]*)$/i.exec(t);
|
|
1778
|
+
let name = as === null ? t : t.slice(0, as.index).trim();
|
|
1779
|
+
if (unqualifyColumn(name) === "*") return { name: "", qualified: "", alias: "", star: true };
|
|
1780
|
+
const qualified = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? ident(name) : "";
|
|
1781
|
+
const plain = unqualifyColumn(ident(name));
|
|
1782
|
+
return { name: plain, qualified, alias: as === null ? plain : ident(as[1] ?? ""), star: false };
|
|
1783
|
+
}
|
|
1520
1784
|
function project(rows, selectRaw) {
|
|
1521
1785
|
if (/^count\s*\(\s*\*/i.test(selectRaw) || /^count\s*\(\s*1\)/i.test(selectRaw)) {
|
|
1522
1786
|
const alias = selectRaw.match(/\bAS\s+([A-Za-z_][A-Za-z0-9_]*)/i)?.[1] ?? "n";
|
|
@@ -1527,10 +1791,21 @@ function project(rows, selectRaw) {
|
|
|
1527
1791
|
return [{ [constMatch[2]]: Number(constMatch[1]) }];
|
|
1528
1792
|
}
|
|
1529
1793
|
if (selectRaw.trim() === "*") return rows;
|
|
1530
|
-
const
|
|
1794
|
+
const items = selectRaw.split(",").map(parseSelectItem);
|
|
1795
|
+
const valueOf = (row, item) => item.qualified !== "" && row[item.qualified] !== void 0 ? row[item.qualified] : row[item.name];
|
|
1796
|
+
if (items.some((item) => item.star)) {
|
|
1797
|
+
return rows.map((row) => {
|
|
1798
|
+
const out = { ...row };
|
|
1799
|
+
for (const item of items) {
|
|
1800
|
+
if (item.star) continue;
|
|
1801
|
+
out[item.alias] = valueOf(row, item);
|
|
1802
|
+
}
|
|
1803
|
+
return out;
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1531
1806
|
return rows.map((row) => {
|
|
1532
1807
|
const out = {};
|
|
1533
|
-
for (const
|
|
1808
|
+
for (const item of items) out[item.alias] = valueOf(row, item);
|
|
1534
1809
|
return out;
|
|
1535
1810
|
});
|
|
1536
1811
|
}
|
|
@@ -3710,6 +3985,7 @@ function createBearerUserResolver(issuer) {
|
|
|
3710
3985
|
}
|
|
3711
3986
|
async function collectFunctions(dir) {
|
|
3712
3987
|
const files = {};
|
|
3988
|
+
const isRuntimeSource = (name) => (name.endsWith(".ts") || name.endsWith(".js")) && !name.endsWith(".d.ts") && !name.endsWith(".test.ts");
|
|
3713
3989
|
const walk = async (sub, prefix) => {
|
|
3714
3990
|
let entries;
|
|
3715
3991
|
try {
|
|
@@ -3722,7 +3998,7 @@ async function collectFunctions(dir) {
|
|
|
3722
3998
|
const full = join8(sub, entry.name);
|
|
3723
3999
|
if (entry.isDirectory()) {
|
|
3724
4000
|
await walk(full, rel);
|
|
3725
|
-
} else if (entry.isFile() && entry.name
|
|
4001
|
+
} else if (entry.isFile() && isRuntimeSource(entry.name)) {
|
|
3726
4002
|
files[rel] = await readFile5(full, "utf8");
|
|
3727
4003
|
}
|
|
3728
4004
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adep/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "`adep` 命令行工具包位:登录 / 初始化 / 本地调试 / 部署 / 数据库维护 / 云存储 / 静态托管 / 微前端组件(widget),实现见任务单 CLI-001 ~ CLI-003 与 FE-002;子路径 `@adep/cli/vite` 提供云函数 vite 插件(CLI-014)",
|
|
6
6
|
"bin": {
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"commander": "14.0.2",
|
|
22
22
|
"esbuild": "0.25.12",
|
|
23
23
|
"@adep/cf-compat": "0.1.1",
|
|
24
|
-
"@adep/vite-plugin": "0.1.
|
|
24
|
+
"@adep/vite-plugin": "0.1.4"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@adep/runtime": "0.2.
|
|
27
|
+
"@adep/runtime": "0.2.4",
|
|
28
28
|
"@adep/types": "0.2.3"
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|