@adep/cli 0.4.4 → 0.4.5
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/README.md +107 -11
- package/dist/index.js +33409 -27038
- package/dist/vite.js +209 -15
- package/dist/worker-entry.js +163 -1
- package/package.json +5 -4
- package/templates/app-desktop/README.md +67 -2
- package/templates/app-desktop/identity-store.mjs +309 -0
- package/templates/app-desktop/identity.mjs +517 -0
- package/templates/app-desktop/main.cjs +17 -0
- package/templates/app-desktop/main.mjs +218 -2
- package/templates/app-desktop/offline-runtime.mjs +72 -3
- package/templates/app-desktop/package.json +10 -6
- package/templates/app-desktop/recovery-ui.mjs +300 -0
- package/templates/app-desktop/sync-loop.mjs +137 -0
- package/templates/app-mobile/README.md +47 -9
- package/templates/app-mobile/package.json +2 -1
- package/templates/app-mobile/plugins/adep-bridge/Package.swift +5 -1
- package/templates/app-mobile/plugins/adep-bridge/android/build.gradle +34 -3
- package/templates/app-mobile/plugins/adep-bridge/android/src/main/AndroidManifest.xml +12 -5
- package/templates/app-mobile/plugins/adep-bridge/android/src/main/java/com/adep/bridge/AdepBridge.kt +198 -12
- package/templates/app-mobile/plugins/adep-bridge/android/src/main/java/com/adep/bridge/AdepIdentity.kt +342 -0
- package/templates/app-mobile/plugins/adep-bridge/android/src/main/java/com/adep/bridge/AdepKeystore.kt +56 -2
- package/templates/app-mobile/plugins/adep-bridge/android/src/main/java/com/adep/bridge/AdepSqlite.kt +506 -0
- package/templates/app-mobile/plugins/adep-bridge/android/src/main/java/com/adep/bridge/AdepSqliteLogic.kt +235 -0
- package/templates/app-mobile/plugins/adep-bridge/dist/plugin.js +30 -0
- package/templates/app-mobile/plugins/adep-bridge/ios/AdepBridge.podspec +3 -0
- package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepBridge.swift +221 -9
- package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepIdentity.swift +500 -0
- package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepKeychain.swift +69 -17
- package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepPermissionGate.swift +25 -4
- package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepShellConfig.swift +54 -0
- package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepSqlite.swift +614 -0
- package/templates/app-mobile/plugins/adep-bridge/package.json +10 -0
- package/templates/app-mobile/template.json +12 -3
- package/templates/app-mobile/web/adep-bridge.js +103 -9
- package/templates/app-mobile/web/offline-runtime.js +230 -0
- package/templates/app-mobile-xtool/AdepShell-Info.plist +24 -0
- package/templates/app-mobile-xtool/Package.swift +59 -0
- package/templates/app-mobile-xtool/README.md +81 -0
- package/templates/app-mobile-xtool/Sources/AdepShell/App.swift +36 -0
- package/templates/app-mobile-xtool/Sources/AdepShell/ShellViewController.swift +223 -0
- package/templates/app-mobile-xtool/adep.permissions.json +4 -0
- package/templates/app-mobile-xtool/app/site/.gitkeep +0 -0
- package/templates/app-mobile-xtool/template.json +15 -0
- package/templates/app-mobile-xtool/xtool.yml +35 -0
- package/templates/app-mobile/plugins/adep-bridge/capacitor.plugin.json +0 -10
package/dist/vite.js
CHANGED
|
@@ -742,10 +742,21 @@ function compileSelect(state, dialect) {
|
|
|
742
742
|
if (state.orderBys.length > 0) {
|
|
743
743
|
sql += ` ORDER BY ${state.orderBys.map((order) => `${dialect.quote(order.column)} ${order.direction.toUpperCase()}`).join(", ")}`;
|
|
744
744
|
}
|
|
745
|
-
if (state.limit !== void 0)
|
|
746
|
-
|
|
745
|
+
if (state.limit !== void 0) {
|
|
746
|
+
assertInlineableInt(state.limit, "limit");
|
|
747
|
+
sql += ` LIMIT ${state.limit}`;
|
|
748
|
+
}
|
|
749
|
+
if (state.offset !== void 0) {
|
|
750
|
+
assertInlineableInt(state.offset, "offset");
|
|
751
|
+
sql += ` OFFSET ${state.offset}`;
|
|
752
|
+
}
|
|
747
753
|
return { sql, params };
|
|
748
754
|
}
|
|
755
|
+
function assertInlineableInt(value, name) {
|
|
756
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
757
|
+
throw unsafeOperation(`${name} \u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570`);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
749
760
|
function compileCount(state, dialect) {
|
|
750
761
|
const params = [];
|
|
751
762
|
let sql = `SELECT count(*) AS n FROM ${dialect.quote(state.table)}`;
|
|
@@ -1299,11 +1310,21 @@ function parseWhereClauses(whereRaw, cursor) {
|
|
|
1299
1310
|
const t = part.trim();
|
|
1300
1311
|
if (t.length === 0) continue;
|
|
1301
1312
|
if (/\bIS\s+NULL\b/i.test(t)) {
|
|
1302
|
-
clauses.push({
|
|
1313
|
+
clauses.push({
|
|
1314
|
+
column: unqualifyColumn(ident(t.split(/\s+IS\s+NULL\b/i)[0] ?? "")),
|
|
1315
|
+
operator: "IS NULL",
|
|
1316
|
+
value: null,
|
|
1317
|
+
list: false
|
|
1318
|
+
});
|
|
1303
1319
|
continue;
|
|
1304
1320
|
}
|
|
1305
1321
|
if (/\bIS\s+NOT\s+NULL\b/i.test(t)) {
|
|
1306
|
-
clauses.push({
|
|
1322
|
+
clauses.push({
|
|
1323
|
+
column: unqualifyColumn(ident(t.split(/\s+IS\s+NOT\s+NULL\b/i)[0] ?? "")),
|
|
1324
|
+
operator: "IS NOT NULL",
|
|
1325
|
+
value: null,
|
|
1326
|
+
list: false
|
|
1327
|
+
});
|
|
1307
1328
|
continue;
|
|
1308
1329
|
}
|
|
1309
1330
|
const opMatch = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_.]*))\s*(=|!=|>=|<=|>|<|like|in|not\s+in)\s*(.+)$/i.exec(
|
|
@@ -1480,7 +1501,10 @@ function parseConflictClause(clause, cursor, fallbackCols) {
|
|
|
1480
1501
|
clause.trim()
|
|
1481
1502
|
);
|
|
1482
1503
|
if (m === null) {
|
|
1483
|
-
throw new SimDbError(
|
|
1504
|
+
throw new SimDbError(
|
|
1505
|
+
"DB_UNSAFE_OP",
|
|
1506
|
+
`\u65E0\u6CD5\u89E3\u6790 ON CONFLICT \u5B50\u53E5\uFF1A${clause.trim().slice(0, 60)}\u2026`
|
|
1507
|
+
);
|
|
1484
1508
|
}
|
|
1485
1509
|
const colsRaw = m[1];
|
|
1486
1510
|
const cols = colsRaw === void 0 ? fallbackCols : colsRaw.split(",").map(ident);
|
|
@@ -1684,16 +1708,16 @@ var SimSqlEngine = class {
|
|
|
1684
1708
|
row[col] = values[i] ?? null;
|
|
1685
1709
|
});
|
|
1686
1710
|
if (conflictSpec !== null) {
|
|
1687
|
-
const existing = table.rows.find(
|
|
1688
|
-
(r) => conflictSpec.cols.every((c) => r[c] === row[c])
|
|
1689
|
-
);
|
|
1711
|
+
const existing = table.rows.find((r) => conflictSpec.cols.every((c) => r[c] === row[c]));
|
|
1690
1712
|
if (existing !== void 0) {
|
|
1691
1713
|
const oldRow = { ...existing };
|
|
1692
1714
|
for (const set of conflictSpec.sets) {
|
|
1693
1715
|
existing[set.col] = resolveSetValue(set.value, oldRow, row);
|
|
1694
1716
|
}
|
|
1695
1717
|
inserted += 1;
|
|
1696
|
-
|
|
1718
|
+
this.persist().catch((error) => {
|
|
1719
|
+
console.warn("[sim] \u6301\u4E45\u5316\u5931\u8D25\uFF1A", error);
|
|
1720
|
+
});
|
|
1697
1721
|
continue;
|
|
1698
1722
|
}
|
|
1699
1723
|
}
|
|
@@ -1701,7 +1725,9 @@ var SimSqlEngine = class {
|
|
|
1701
1725
|
table.rows.push(row);
|
|
1702
1726
|
inserted += 1;
|
|
1703
1727
|
}
|
|
1704
|
-
|
|
1728
|
+
this.persist().catch((error) => {
|
|
1729
|
+
console.warn("[sim] \u6301\u4E45\u5316\u5931\u8D25\uFF1A", error);
|
|
1730
|
+
});
|
|
1705
1731
|
return { changes: inserted };
|
|
1706
1732
|
}
|
|
1707
1733
|
execUpdate(sql, params) {
|
|
@@ -1731,7 +1757,9 @@ var SimSqlEngine = class {
|
|
|
1731
1757
|
}
|
|
1732
1758
|
changes += 1;
|
|
1733
1759
|
}
|
|
1734
|
-
|
|
1760
|
+
this.persist().catch((error) => {
|
|
1761
|
+
console.warn("[sim] \u6301\u4E45\u5316\u5931\u8D25\uFF1A", error);
|
|
1762
|
+
});
|
|
1735
1763
|
return { changes };
|
|
1736
1764
|
}
|
|
1737
1765
|
execDelete(sql, params) {
|
|
@@ -1749,7 +1777,9 @@ var SimSqlEngine = class {
|
|
|
1749
1777
|
else changes += 1;
|
|
1750
1778
|
}
|
|
1751
1779
|
table.rows = keep;
|
|
1752
|
-
|
|
1780
|
+
this.persist().catch((error) => {
|
|
1781
|
+
console.warn("[sim] \u6301\u4E45\u5316\u5931\u8D25\uFF1A", error);
|
|
1782
|
+
});
|
|
1753
1783
|
return { changes };
|
|
1754
1784
|
}
|
|
1755
1785
|
select(sql, params) {
|
|
@@ -1771,7 +1801,9 @@ var SimSqlEngine = class {
|
|
|
1771
1801
|
rows = table.rows.map((row) => {
|
|
1772
1802
|
const merged = { ...row };
|
|
1773
1803
|
for (const j of joins) {
|
|
1774
|
-
const joined = this.tables[j.table]?.rows.find(
|
|
1804
|
+
const joined = this.tables[j.table]?.rows.find(
|
|
1805
|
+
(jr) => jr[j.leftCol] === merged[j.rightCol]
|
|
1806
|
+
);
|
|
1775
1807
|
if (joined !== void 0) {
|
|
1776
1808
|
for (const [k, v] of Object.entries(joined)) merged[`${j.alias}.${k}`] = v;
|
|
1777
1809
|
}
|
|
@@ -2872,7 +2904,7 @@ function envDbFile() {
|
|
|
2872
2904
|
function displayPath(cwd, target) {
|
|
2873
2905
|
if (target === ":memory:") return target;
|
|
2874
2906
|
const rel = relative(cwd, target);
|
|
2875
|
-
return rel.startsWith("..") ? target : rel;
|
|
2907
|
+
return rel.startsWith("..") ? target : rel.split(/[\\/]/u).join("/");
|
|
2876
2908
|
}
|
|
2877
2909
|
function simDbTarget(cwd) {
|
|
2878
2910
|
return displayPath(cwd, join5(simDir(cwd), "db.json"));
|
|
@@ -3660,6 +3692,158 @@ function parseExportClause(clause) {
|
|
|
3660
3692
|
}
|
|
3661
3693
|
return entries;
|
|
3662
3694
|
}
|
|
3695
|
+
var CONTINUES_AFTER = "=+-*/%&|^<>?:,.[(";
|
|
3696
|
+
var CONTINUES_BEFORE = "`([.?:,+-*/%&|^<>=";
|
|
3697
|
+
var REGEX_PRECEDERS = "(,=:[!&|?{};+-*%~^<>";
|
|
3698
|
+
var MATCHING_OPEN = { ")": "(", "]": "[", "}": "{" };
|
|
3699
|
+
function lineNumberAt(code, index) {
|
|
3700
|
+
let line = 1;
|
|
3701
|
+
for (let i = 0; i < index; i += 1) if (code.charCodeAt(i) === 10) line += 1;
|
|
3702
|
+
return line;
|
|
3703
|
+
}
|
|
3704
|
+
function skipQuoted(code, start) {
|
|
3705
|
+
const quote = code.charAt(start);
|
|
3706
|
+
let i = start + 1;
|
|
3707
|
+
while (i < code.length) {
|
|
3708
|
+
const ch = code.charAt(i);
|
|
3709
|
+
if (ch === "\\") {
|
|
3710
|
+
i += 2;
|
|
3711
|
+
continue;
|
|
3712
|
+
}
|
|
3713
|
+
if (ch === quote) return i + 1;
|
|
3714
|
+
if (ch === "\n") return i;
|
|
3715
|
+
i += 1;
|
|
3716
|
+
}
|
|
3717
|
+
return code.length;
|
|
3718
|
+
}
|
|
3719
|
+
function skipRegex(code, start) {
|
|
3720
|
+
let i = start + 1;
|
|
3721
|
+
let inClass = false;
|
|
3722
|
+
while (i < code.length) {
|
|
3723
|
+
const ch = code.charAt(i);
|
|
3724
|
+
if (ch === "\\") {
|
|
3725
|
+
i += 2;
|
|
3726
|
+
continue;
|
|
3727
|
+
}
|
|
3728
|
+
if (ch === "\n") return start + 1;
|
|
3729
|
+
if (ch === "[") inClass = true;
|
|
3730
|
+
else if (ch === "]") inClass = false;
|
|
3731
|
+
else if (ch === "/" && !inClass) return i + 1;
|
|
3732
|
+
i += 1;
|
|
3733
|
+
}
|
|
3734
|
+
return code.length;
|
|
3735
|
+
}
|
|
3736
|
+
function regexCanStart(prev) {
|
|
3737
|
+
return prev === "" || REGEX_PRECEDERS.includes(prev);
|
|
3738
|
+
}
|
|
3739
|
+
function continuesOnto(code, newline, prev) {
|
|
3740
|
+
if (prev !== "" && CONTINUES_AFTER.includes(prev)) return true;
|
|
3741
|
+
let i = newline + 1;
|
|
3742
|
+
while (i < code.length && /\s/.test(code.charAt(i))) i += 1;
|
|
3743
|
+
const next = code.charAt(i);
|
|
3744
|
+
return next !== "" && CONTINUES_BEFORE.includes(next);
|
|
3745
|
+
}
|
|
3746
|
+
function scanDeclaration(code, from) {
|
|
3747
|
+
const commas = [];
|
|
3748
|
+
const stack = [];
|
|
3749
|
+
let prev = "";
|
|
3750
|
+
let i = from;
|
|
3751
|
+
while (i < code.length) {
|
|
3752
|
+
const ch = code.charAt(i);
|
|
3753
|
+
if (stack[stack.length - 1] === "`") {
|
|
3754
|
+
if (ch === "\\") {
|
|
3755
|
+
i += 2;
|
|
3756
|
+
continue;
|
|
3757
|
+
}
|
|
3758
|
+
if (ch === "`") {
|
|
3759
|
+
stack.pop();
|
|
3760
|
+
prev = "`";
|
|
3761
|
+
i += 1;
|
|
3762
|
+
continue;
|
|
3763
|
+
}
|
|
3764
|
+
if (ch === "$" && code.charAt(i + 1) === "{") {
|
|
3765
|
+
stack.push("{");
|
|
3766
|
+
prev = "{";
|
|
3767
|
+
i += 2;
|
|
3768
|
+
continue;
|
|
3769
|
+
}
|
|
3770
|
+
i += 1;
|
|
3771
|
+
continue;
|
|
3772
|
+
}
|
|
3773
|
+
if (ch === "/" && code.charAt(i + 1) === "/") {
|
|
3774
|
+
const newline = code.indexOf("\n", i);
|
|
3775
|
+
i = newline === -1 ? code.length : newline;
|
|
3776
|
+
continue;
|
|
3777
|
+
}
|
|
3778
|
+
if (ch === "/" && code.charAt(i + 1) === "*") {
|
|
3779
|
+
const close = code.indexOf("*/", i + 2);
|
|
3780
|
+
i = close === -1 ? code.length : close + 2;
|
|
3781
|
+
continue;
|
|
3782
|
+
}
|
|
3783
|
+
if (ch === '"' || ch === "'") {
|
|
3784
|
+
i = skipQuoted(code, i);
|
|
3785
|
+
prev = ch;
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
if (ch === "`") {
|
|
3789
|
+
stack.push("`");
|
|
3790
|
+
i += 1;
|
|
3791
|
+
continue;
|
|
3792
|
+
}
|
|
3793
|
+
if (ch === "/" && regexCanStart(prev)) {
|
|
3794
|
+
i = skipRegex(code, i);
|
|
3795
|
+
prev = "/";
|
|
3796
|
+
continue;
|
|
3797
|
+
}
|
|
3798
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
3799
|
+
stack.push(ch);
|
|
3800
|
+
prev = ch;
|
|
3801
|
+
i += 1;
|
|
3802
|
+
continue;
|
|
3803
|
+
}
|
|
3804
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
3805
|
+
if (stack[stack.length - 1] !== MATCHING_OPEN[ch]) return { commas, end: i };
|
|
3806
|
+
stack.pop();
|
|
3807
|
+
prev = ch;
|
|
3808
|
+
i += 1;
|
|
3809
|
+
continue;
|
|
3810
|
+
}
|
|
3811
|
+
if (stack.length === 0) {
|
|
3812
|
+
if (ch === ";") return { commas, end: i };
|
|
3813
|
+
if (ch === ",") commas.push(i);
|
|
3814
|
+
if (ch === "\n" && !continuesOnto(code, i, prev)) return { commas, end: i };
|
|
3815
|
+
}
|
|
3816
|
+
if (!/\s/.test(ch)) prev = ch;
|
|
3817
|
+
i += 1;
|
|
3818
|
+
}
|
|
3819
|
+
return { commas, end: code.length };
|
|
3820
|
+
}
|
|
3821
|
+
function declaratorName(segment) {
|
|
3822
|
+
const match = /^\s*([A-Za-z_$][\w$]*)\s*(?:=(?![=>])|$)/.exec(segment);
|
|
3823
|
+
return match === null ? null : match[1] ?? null;
|
|
3824
|
+
}
|
|
3825
|
+
function restDeclarators(code, from) {
|
|
3826
|
+
const { commas, end } = scanDeclaration(code, from);
|
|
3827
|
+
if (commas.length === 0) return [];
|
|
3828
|
+
const segments = [];
|
|
3829
|
+
let cursor = from;
|
|
3830
|
+
for (const comma of commas) {
|
|
3831
|
+
segments.push(code.slice(cursor, comma));
|
|
3832
|
+
cursor = comma + 1;
|
|
3833
|
+
}
|
|
3834
|
+
segments.push(code.slice(cursor, end));
|
|
3835
|
+
const names = [];
|
|
3836
|
+
for (const segment of segments.slice(1)) {
|
|
3837
|
+
const name = declaratorName(segment);
|
|
3838
|
+
if (name === null) {
|
|
3839
|
+
throw new Error(
|
|
3840
|
+
`\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u58F0\u660E\u5F62\u6001\uFF1A"${segment.trim()}"\uFF08\u6C99\u7BB1\u5B50\u96C6\u4EC5\u63A5\u53D7\u9017\u53F7\u5206\u9694\u7684\u7B80\u5355\u6807\u8BC6\u7B26\u58F0\u660E\uFF0C\u5982 \`export const a = 1, b = 2\`\uFF1B\u542B\u89E3\u6784\u6216\u590D\u6742\u521D\u503C\u65F6\u8BF7\u62C6\u6210\u591A\u6761 export\uFF09`
|
|
3841
|
+
);
|
|
3842
|
+
}
|
|
3843
|
+
names.push(name);
|
|
3844
|
+
}
|
|
3845
|
+
return names;
|
|
3846
|
+
}
|
|
3663
3847
|
function transformModule(code) {
|
|
3664
3848
|
let out = code;
|
|
3665
3849
|
const named = [];
|
|
@@ -3722,10 +3906,20 @@ function transformModule(code) {
|
|
|
3722
3906
|
return `${lead}${indent}const ${temp} = require(${source})${pad}`;
|
|
3723
3907
|
}
|
|
3724
3908
|
);
|
|
3909
|
+
const destructured = /(^|\n)[ \t]*export\s+(const|let|var)\s*[{[]/.exec(out);
|
|
3910
|
+
if (destructured !== null) {
|
|
3911
|
+
const offset = destructured.index + (destructured[1] === "\n" ? 1 : 0);
|
|
3912
|
+
throw new Error(
|
|
3913
|
+
`\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u58F0\u660E\u5F62\u6001\uFF08\u7B2C ${lineNumberAt(out, offset)} \u884C\uFF09\uFF1A\u89E3\u6784\u5BFC\u51FA \`export ${destructured[2] ?? ""} { \u2026 } = \u2026\`\u3002\u6C99\u7BB1\u5B50\u96C6\u4EC5\u63A5\u53D7\u7B80\u5355\u6807\u8BC6\u7B26\u58F0\u660E\uFF1A\u8BF7\u5148\u58F0\u660E\u518D \`export { a, b }\`\uFF0C\u6216\u62C6\u6210\u591A\u6761 \`export const\``
|
|
3914
|
+
);
|
|
3915
|
+
}
|
|
3725
3916
|
out = out.replace(
|
|
3726
3917
|
/export\s+(const|let|var)\s+([A-Za-z_$][\w$]*)/g,
|
|
3727
|
-
(
|
|
3918
|
+
(match, kw, name, offset, source) => {
|
|
3728
3919
|
named.push({ local: name, exported: name });
|
|
3920
|
+
for (const extra of restDeclarators(source, offset + match.length)) {
|
|
3921
|
+
named.push({ local: extra, exported: extra });
|
|
3922
|
+
}
|
|
3729
3923
|
return `${kw} ${name}`;
|
|
3730
3924
|
}
|
|
3731
3925
|
);
|
package/dist/worker-entry.js
CHANGED
|
@@ -127,6 +127,158 @@ function parseExportClause(clause) {
|
|
|
127
127
|
}
|
|
128
128
|
return entries;
|
|
129
129
|
}
|
|
130
|
+
var CONTINUES_AFTER = "=+-*/%&|^<>?:,.[(";
|
|
131
|
+
var CONTINUES_BEFORE = "`([.?:,+-*/%&|^<>=";
|
|
132
|
+
var REGEX_PRECEDERS = "(,=:[!&|?{};+-*%~^<>";
|
|
133
|
+
var MATCHING_OPEN = { ")": "(", "]": "[", "}": "{" };
|
|
134
|
+
function lineNumberAt(code, index) {
|
|
135
|
+
let line = 1;
|
|
136
|
+
for (let i = 0; i < index; i += 1) if (code.charCodeAt(i) === 10) line += 1;
|
|
137
|
+
return line;
|
|
138
|
+
}
|
|
139
|
+
function skipQuoted(code, start) {
|
|
140
|
+
const quote = code.charAt(start);
|
|
141
|
+
let i = start + 1;
|
|
142
|
+
while (i < code.length) {
|
|
143
|
+
const ch = code.charAt(i);
|
|
144
|
+
if (ch === "\\") {
|
|
145
|
+
i += 2;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (ch === quote) return i + 1;
|
|
149
|
+
if (ch === "\n") return i;
|
|
150
|
+
i += 1;
|
|
151
|
+
}
|
|
152
|
+
return code.length;
|
|
153
|
+
}
|
|
154
|
+
function skipRegex(code, start) {
|
|
155
|
+
let i = start + 1;
|
|
156
|
+
let inClass = false;
|
|
157
|
+
while (i < code.length) {
|
|
158
|
+
const ch = code.charAt(i);
|
|
159
|
+
if (ch === "\\") {
|
|
160
|
+
i += 2;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (ch === "\n") return start + 1;
|
|
164
|
+
if (ch === "[") inClass = true;
|
|
165
|
+
else if (ch === "]") inClass = false;
|
|
166
|
+
else if (ch === "/" && !inClass) return i + 1;
|
|
167
|
+
i += 1;
|
|
168
|
+
}
|
|
169
|
+
return code.length;
|
|
170
|
+
}
|
|
171
|
+
function regexCanStart(prev) {
|
|
172
|
+
return prev === "" || REGEX_PRECEDERS.includes(prev);
|
|
173
|
+
}
|
|
174
|
+
function continuesOnto(code, newline, prev) {
|
|
175
|
+
if (prev !== "" && CONTINUES_AFTER.includes(prev)) return true;
|
|
176
|
+
let i = newline + 1;
|
|
177
|
+
while (i < code.length && /\s/.test(code.charAt(i))) i += 1;
|
|
178
|
+
const next = code.charAt(i);
|
|
179
|
+
return next !== "" && CONTINUES_BEFORE.includes(next);
|
|
180
|
+
}
|
|
181
|
+
function scanDeclaration(code, from) {
|
|
182
|
+
const commas = [];
|
|
183
|
+
const stack = [];
|
|
184
|
+
let prev = "";
|
|
185
|
+
let i = from;
|
|
186
|
+
while (i < code.length) {
|
|
187
|
+
const ch = code.charAt(i);
|
|
188
|
+
if (stack[stack.length - 1] === "`") {
|
|
189
|
+
if (ch === "\\") {
|
|
190
|
+
i += 2;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (ch === "`") {
|
|
194
|
+
stack.pop();
|
|
195
|
+
prev = "`";
|
|
196
|
+
i += 1;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (ch === "$" && code.charAt(i + 1) === "{") {
|
|
200
|
+
stack.push("{");
|
|
201
|
+
prev = "{";
|
|
202
|
+
i += 2;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
i += 1;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (ch === "/" && code.charAt(i + 1) === "/") {
|
|
209
|
+
const newline = code.indexOf("\n", i);
|
|
210
|
+
i = newline === -1 ? code.length : newline;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (ch === "/" && code.charAt(i + 1) === "*") {
|
|
214
|
+
const close = code.indexOf("*/", i + 2);
|
|
215
|
+
i = close === -1 ? code.length : close + 2;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (ch === '"' || ch === "'") {
|
|
219
|
+
i = skipQuoted(code, i);
|
|
220
|
+
prev = ch;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (ch === "`") {
|
|
224
|
+
stack.push("`");
|
|
225
|
+
i += 1;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (ch === "/" && regexCanStart(prev)) {
|
|
229
|
+
i = skipRegex(code, i);
|
|
230
|
+
prev = "/";
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
234
|
+
stack.push(ch);
|
|
235
|
+
prev = ch;
|
|
236
|
+
i += 1;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
240
|
+
if (stack[stack.length - 1] !== MATCHING_OPEN[ch]) return { commas, end: i };
|
|
241
|
+
stack.pop();
|
|
242
|
+
prev = ch;
|
|
243
|
+
i += 1;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (stack.length === 0) {
|
|
247
|
+
if (ch === ";") return { commas, end: i };
|
|
248
|
+
if (ch === ",") commas.push(i);
|
|
249
|
+
if (ch === "\n" && !continuesOnto(code, i, prev)) return { commas, end: i };
|
|
250
|
+
}
|
|
251
|
+
if (!/\s/.test(ch)) prev = ch;
|
|
252
|
+
i += 1;
|
|
253
|
+
}
|
|
254
|
+
return { commas, end: code.length };
|
|
255
|
+
}
|
|
256
|
+
function declaratorName(segment) {
|
|
257
|
+
const match = /^\s*([A-Za-z_$][\w$]*)\s*(?:=(?![=>])|$)/.exec(segment);
|
|
258
|
+
return match === null ? null : match[1] ?? null;
|
|
259
|
+
}
|
|
260
|
+
function restDeclarators(code, from) {
|
|
261
|
+
const { commas, end } = scanDeclaration(code, from);
|
|
262
|
+
if (commas.length === 0) return [];
|
|
263
|
+
const segments = [];
|
|
264
|
+
let cursor = from;
|
|
265
|
+
for (const comma of commas) {
|
|
266
|
+
segments.push(code.slice(cursor, comma));
|
|
267
|
+
cursor = comma + 1;
|
|
268
|
+
}
|
|
269
|
+
segments.push(code.slice(cursor, end));
|
|
270
|
+
const names = [];
|
|
271
|
+
for (const segment of segments.slice(1)) {
|
|
272
|
+
const name = declaratorName(segment);
|
|
273
|
+
if (name === null) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u58F0\u660E\u5F62\u6001\uFF1A"${segment.trim()}"\uFF08\u6C99\u7BB1\u5B50\u96C6\u4EC5\u63A5\u53D7\u9017\u53F7\u5206\u9694\u7684\u7B80\u5355\u6807\u8BC6\u7B26\u58F0\u660E\uFF0C\u5982 \`export const a = 1, b = 2\`\uFF1B\u542B\u89E3\u6784\u6216\u590D\u6742\u521D\u503C\u65F6\u8BF7\u62C6\u6210\u591A\u6761 export\uFF09`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
names.push(name);
|
|
279
|
+
}
|
|
280
|
+
return names;
|
|
281
|
+
}
|
|
130
282
|
function transformModule(code) {
|
|
131
283
|
let out = code;
|
|
132
284
|
const named = [];
|
|
@@ -189,10 +341,20 @@ function transformModule(code) {
|
|
|
189
341
|
return `${lead}${indent}const ${temp} = require(${source})${pad}`;
|
|
190
342
|
}
|
|
191
343
|
);
|
|
344
|
+
const destructured = /(^|\n)[ \t]*export\s+(const|let|var)\s*[{[]/.exec(out);
|
|
345
|
+
if (destructured !== null) {
|
|
346
|
+
const offset = destructured.index + (destructured[1] === "\n" ? 1 : 0);
|
|
347
|
+
throw new Error(
|
|
348
|
+
`\u4E0D\u652F\u6301\u7684\u5BFC\u51FA\u58F0\u660E\u5F62\u6001\uFF08\u7B2C ${lineNumberAt(out, offset)} \u884C\uFF09\uFF1A\u89E3\u6784\u5BFC\u51FA \`export ${destructured[2] ?? ""} { \u2026 } = \u2026\`\u3002\u6C99\u7BB1\u5B50\u96C6\u4EC5\u63A5\u53D7\u7B80\u5355\u6807\u8BC6\u7B26\u58F0\u660E\uFF1A\u8BF7\u5148\u58F0\u660E\u518D \`export { a, b }\`\uFF0C\u6216\u62C6\u6210\u591A\u6761 \`export const\``
|
|
349
|
+
);
|
|
350
|
+
}
|
|
192
351
|
out = out.replace(
|
|
193
352
|
/export\s+(const|let|var)\s+([A-Za-z_$][\w$]*)/g,
|
|
194
|
-
(
|
|
353
|
+
(match, kw, name, offset, source) => {
|
|
195
354
|
named.push({ local: name, exported: name });
|
|
355
|
+
for (const extra of restDeclarators(source, offset + match.length)) {
|
|
356
|
+
named.push({ local: extra, exported: extra });
|
|
357
|
+
}
|
|
196
358
|
return `${kw} ${name}`;
|
|
197
359
|
}
|
|
198
360
|
);
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"homepage": "https://adep.top",
|
|
9
9
|
"bugs": "https://gitcode.com/yunqiang_wu/ai-yunke/issues",
|
|
10
|
-
"version": "0.4.
|
|
10
|
+
"version": "0.4.5",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"description": "`adep` 命令行工具包位:登录 / 初始化 / 本地调试 / 部署 / 数据库维护 / 云存储 / 静态托管 / 微前端组件(widget),实现见任务单 CLI-001 ~ CLI-003 与 FE-002;子路径 `@adep/cli/vite` 提供云函数 vite 插件(CLI-014)",
|
|
13
13
|
"bin": {
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
"commander": "14.0.2",
|
|
30
30
|
"esbuild": "0.25.12",
|
|
31
31
|
"yaml": "2.9.1",
|
|
32
|
-
"zod": "^4.
|
|
33
|
-
"@adep/cf-compat": "0.1.
|
|
34
|
-
"@adep/runtime": "0.
|
|
32
|
+
"zod": "^4.6.6",
|
|
33
|
+
"@adep/cf-compat": "0.1.4",
|
|
34
|
+
"@adep/app-runtime": "0.1.1",
|
|
35
|
+
"@adep/runtime": "0.3.5",
|
|
35
36
|
"@adep/crypto": "0.1.1",
|
|
36
37
|
"@adep/vite-plugin": "0.1.9"
|
|
37
38
|
},
|
|
@@ -18,7 +18,7 @@ GUI(前端产物)随包本地加载,云函数经 HTTP 在服务端执行
|
|
|
18
18
|
|
|
19
19
|
1. 把平台项目的前端构建产物放进 `app/site/`(`adep app build` 会自动拉取已发布产物);
|
|
20
20
|
2. `npm install`;
|
|
21
|
-
3. `npm start
|
|
21
|
+
3. `npm start`(或仓库根的 `adep app open`,等价——直接调壳工程内的 electron;配置从 `adep.app.json` 读取,无需环境变量)。
|
|
22
22
|
|
|
23
23
|
## 页面侧调用云函数(在线运行时)
|
|
24
24
|
|
|
@@ -31,12 +31,77 @@ const client = createAdepClient({ baseUrl: window.__ADEP_APP__.platformUrl })
|
|
|
31
31
|
const result = await client.invokeFunction('hello', { name: 'world' })
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## 设备身份与同步(离线构建之后)
|
|
35
|
+
|
|
36
|
+
离线构建把设备端运行时注入 `app/offline/`,首次启动时在 `data/offline.sqlite` 建本地库:
|
|
37
|
+
白名单函数在本地执行,写 owned 表时同时追加变更流。
|
|
38
|
+
|
|
39
|
+
**设备身份是首启自动签发的**(APP-033 / PRD §2.11.5):主进程启动后在后台调一次
|
|
40
|
+
`POST /api/v1/projects/<slug>/app-installations`(`platform` 按**宿主 OS** 映射为
|
|
41
|
+
`macos` / `windows` / `linux`——服务端没有 `desktop` 这个取值),拿到 `ownerKey` +
|
|
42
|
+
一次性恢复码后写进受保护存储;此后每次启动只读不发请求。
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
identity.mjs 判定层(配置 / 端点 / 形态 / 响应解析 / 平台映射)+ 自举流程
|
|
46
|
+
identity-store.mjs 受保护存储:macOS 钥匙串 → Linux Secret Service → 0600 文件(明说降级)
|
|
47
|
+
recovery-ui.mjs 恢复码呈现与导出(APP-034;全在主进程,页面侧拿不到)
|
|
48
|
+
sync-loop.mjs 后台同步驱动(首轮延迟 → 每 5 分钟一轮;不重叠、失败不致命)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
存储后端:
|
|
52
|
+
|
|
53
|
+
- macOS:`security add-generic-password`(登录钥匙串);
|
|
54
|
+
- Linux:`secret-tool`(Secret Service);
|
|
55
|
+
- **Windows 与「系统库不可用」**:退化到 `data/identity/` 下的 0600 文件,并在日志与
|
|
56
|
+
`adep app doctor` 里**明说「非系统凭据」**——不会静默假装是钥匙串。
|
|
57
|
+
|
|
58
|
+
身份状态(**非机密**:只有存储类型 / 原因码 / 安装 id / 时间)写在
|
|
59
|
+
`data/identity/status.json`,`adep app doctor` 的 `identity` 项读它:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
adep app doctor # 已签发(系统凭据库 keychain) / 非系统凭据(file) / 首启签发失败(原因码)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
⚠️ **页面侧拿不到 `ownerKey`**(与移动壳同一条边界):同步跑在主进程,渲染进程不需要这枚
|
|
66
|
+
bearer 凭据,给它等于「任意 XSS 都能劫持该设备的数据分区」。
|
|
67
|
+
|
|
68
|
+
## 恢复码(APP-034:呈现 / 再次查看 / 导出)
|
|
69
|
+
|
|
70
|
+
签发响应里的恢复码**只出现一次**(服务端只存摘要、任何端点不重发),壳负责把它递到用户手里:
|
|
71
|
+
|
|
72
|
+
- **首次呈现**:自举成功后自动弹一次原生对话框(码可读且没呈现过才弹——老安装升级上来
|
|
73
|
+
同样有机会看到)。此后不再主动弹,标记写在 `data/identity/recovery-code-shown`;
|
|
74
|
+
- **再次查看 / 导出**:应用菜单 **「凭据」→「查看恢复码…」/「导出恢复码…」**(导出文件
|
|
75
|
+
头部写明「服务端无法重发,丢了码等于放弃认领」);
|
|
76
|
+
- **复制到剪贴板**:两个对话框里都有一键复制。
|
|
77
|
+
|
|
78
|
+
呈现与导出**全在主进程**(应用菜单 + 原生对话框):不开 IPC、不改 preload,恢复码与
|
|
79
|
+
`ownerKey` 一样不进渲染进程。码不可读时(未签发 / 存储失败)对话框会说明原因并指向
|
|
80
|
+
`adep app doctor`,不是静默空窗。
|
|
81
|
+
|
|
82
|
+
身份就绪后主进程自动把同步挂到运行时上:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const runtime = await createOfflineRuntime() // 页面先可用
|
|
86
|
+
const sync = runtime.attachSync({ ownerKey }) // 身份到位后再挂(不重开库)
|
|
87
|
+
await sync.push() // 本地变更流 → outbox → 云端
|
|
88
|
+
await sync.pull() // 云端增量 → 本地库(直写驱动,不回灌 outbox)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
手动同步(开发期 / 排障)仍用 CLI:`adep app sync --push`(身份取 `--owner-key` 或
|
|
92
|
+
`ADEP_OWNER_KEY`)。**不要**在 `adep.app.json` 或环境变量里编一个 key:本地自造会写出一个
|
|
93
|
+
「看起来成功」的空分区(PRD §2.11.5 明确「本地自造判违规」)。
|
|
94
|
+
|
|
95
|
+
⚠️ 已知限制(已登记):打包形态下 `data/` 仍在应用目录内(`.app` / Program Files),
|
|
96
|
+
写入可能被系统拒绝——此时 `identity` 项会显示 `IDENTITY_STORE_FAILED`,属于**响亮失败**。
|
|
97
|
+
把本地数据目录迁到 `app.getPath('userData')` 是独立的一批(含 `data/offline.sqlite`)。
|
|
98
|
+
|
|
34
99
|
## 打安装包
|
|
35
100
|
|
|
36
101
|
推荐用 CLI(会自动拉取**已发布**的前端产物、回写产物版本、产出构建报告):
|
|
37
102
|
|
|
38
103
|
```bash
|
|
39
|
-
adep app build # 当前平台、走 package.json build 段的 target(mac dmg / win nsis / linux AppImage)
|
|
104
|
+
adep app build # 当前平台、走 package.json build 段的 target(mac dmg / win nsis+zip / linux AppImage+deb)
|
|
40
105
|
adep app build --target dir # 只出未压缩目录(macOS 即 .app),本地快速验证
|
|
41
106
|
adep app build --prepare-only # 只拉产物 + 注入 + 出构建报告,不调 electron-builder
|
|
42
107
|
```
|