@seasonkoh/webaz 0.1.29 → 0.1.30
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/bond-refund-blockers.js +50 -0
- package/dist/bond-slash.js +100 -0
- package/dist/bond-terms.js +31 -0
- package/dist/direct-pay-bond-rail-clearance.js +10 -5
- package/dist/direct-pay-cancel-refund.js +160 -0
- package/dist/direct-pay-create.js +78 -9
- package/dist/direct-pay-disclosures.js +17 -11
- package/dist/direct-pay-fee-ar.js +2 -2
- package/dist/direct-pay-fee-prepay-request.js +80 -0
- package/dist/direct-pay-returns.js +104 -0
- package/dist/direct-pay-stock.js +9 -0
- package/dist/direct-receive-deferral.js +30 -1
- package/dist/direct-receive-deposits.js +122 -17
- package/dist/free-shipping.js +37 -0
- package/dist/fx-rates.js +7 -0
- package/dist/layer0-foundation/L0-1-database/schema.js +246 -0
- package/dist/layer0-foundation/L0-2-state-machine/engine.js +1 -0
- package/dist/layer0-foundation/L0-2-state-machine/transitions.js +73 -0
- package/dist/layer1-agent/L1-1-mcp-server/server.js +81 -16
- package/dist/layer2-business/L2-6-notifications/notification-engine.js +110 -41
- package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +133 -31
- package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +16 -4
- package/dist/layer3-trust/L3-1-dispute-engine/mutual-cancel.js +156 -0
- package/dist/platform-receive-accounts.js +94 -0
- package/dist/pwa/arbitration-read-admin.js +37 -0
- package/dist/pwa/arbitrator-lifecycle.js +100 -0
- package/dist/pwa/contract-fingerprint.js +15 -0
- package/dist/pwa/direct-pay-order-redaction.js +20 -2
- package/dist/pwa/human-presence.js +2 -6
- package/dist/pwa/public/app-account.js +9 -9
- package/dist/pwa/public/app-admin-disputes.js +55 -0
- package/dist/pwa/public/app-agent-appeal.js +90 -0
- package/dist/pwa/public/app-agent-approvals.js +93 -0
- package/dist/pwa/public/app-agent-pair.js +127 -0
- package/dist/pwa/public/app-arbitrator-admin.js +87 -0
- package/dist/pwa/public/app-arbitrator-entry.js +9 -0
- package/dist/pwa/public/app-bond-deferral-ui.js +9 -0
- package/dist/pwa/public/app-bond-refund-ui.js +66 -0
- package/dist/pwa/public/app-bond-slash-ui.js +74 -0
- package/dist/pwa/public/app-bond-terms-ui.js +23 -0
- package/dist/pwa/public/app-bond-ui.js +108 -0
- package/dist/pwa/public/app-chat-poll.js +6 -6
- package/dist/pwa/public/app-contribution-hub.js +23 -0
- package/dist/pwa/public/app-direct-pay-accounts.js +1 -1
- package/dist/pwa/public/app-direct-pay-buyer.js +1 -1
- package/dist/pwa/public/app-direct-pay-cancel-refund.js +72 -0
- package/dist/pwa/public/app-direct-pay-copy.js +12 -0
- package/dist/pwa/public/app-direct-pay-fee-history.js +39 -0
- package/dist/pwa/public/app-direct-pay-fee-ops.js +1 -1
- package/dist/pwa/public/app-direct-pay-fee-request.js +81 -0
- package/dist/pwa/public/app-direct-pay-fee-requests-admin.js +70 -0
- package/dist/pwa/public/app-direct-pay-memo.js +14 -0
- package/dist/pwa/public/app-direct-pay-negotiation.js +35 -0
- package/dist/pwa/public/app-direct-pay-pay.js +15 -0
- package/dist/pwa/public/app-direct-pay-paymodal.js +32 -0
- package/dist/pwa/public/app-direct-pay-reconcile.js +19 -0
- package/dist/pwa/public/app-direct-pay-returns.js +56 -0
- package/dist/pwa/public/app-direct-pay-reveal.js +82 -0
- package/dist/pwa/public/app-direct-pay-sales-report.js +70 -0
- package/dist/pwa/public/app-direct-pay.js +36 -37
- package/dist/pwa/public/app-dispute-close-ui.js +38 -0
- package/dist/pwa/public/app-free-shipping-ui.js +29 -0
- package/dist/pwa/public/app-gmv-rail-split.js +12 -0
- package/dist/pwa/public/app-listing-commerce-ui.js +72 -0
- package/dist/pwa/public/app-mutual-cancel.js +54 -0
- package/dist/pwa/public/app-notif-templates-orders.js +43 -0
- package/dist/pwa/public/app-notif-templates.js +22 -0
- package/dist/pwa/public/app-order-accept-ui.js +158 -0
- package/dist/pwa/public/app-order-errors.js +50 -0
- package/dist/pwa/public/app-order-labels.js +19 -0
- package/dist/pwa/public/app-order-rail-filter.js +26 -0
- package/dist/pwa/public/app-platform-receive-accounts.js +140 -0
- package/dist/pwa/public/app-poll-governor.js +22 -0
- package/dist/pwa/public/app-profile.js +4 -4
- package/dist/pwa/public/app-purchase-terms-ui.js +68 -0
- package/dist/pwa/public/app-sale-regions-ui.js +38 -0
- package/dist/pwa/public/app-seller.js +1 -1
- package/dist/pwa/public/app-trade-tax-ui.js +33 -0
- package/dist/pwa/public/app.js +133 -160
- package/dist/pwa/public/i18n.js +675 -5
- package/dist/pwa/public/index.html +41 -0
- package/dist/pwa/public/openapi.json +709 -11
- package/dist/pwa/public/style.css +3 -0
- package/dist/pwa/routes/admin-direct-receive-deposits.js +215 -3
- package/dist/pwa/routes/admin-protocol-params.js +16 -0
- package/dist/pwa/routes/admin-reports.js +31 -5
- package/dist/pwa/routes/agent-governance.js +1 -1
- package/dist/pwa/routes/agent-grants.js +253 -32
- package/dist/pwa/routes/analytics.js +2 -0
- package/dist/pwa/routes/arbitrator.js +67 -14
- package/dist/pwa/routes/bond-seller.js +162 -0
- package/dist/pwa/routes/direct-pay-cancel-refund.js +111 -0
- package/dist/pwa/routes/direct-pay-disclosure-acks.js +9 -4
- package/dist/pwa/routes/direct-pay-pending-accept.js +222 -0
- package/dist/pwa/routes/direct-pay-returns.js +74 -0
- package/dist/pwa/routes/direct-pay-timeouts.js +186 -9
- package/dist/pwa/routes/disputes-read.js +20 -6
- package/dist/pwa/routes/disputes-write.js +91 -33
- package/dist/pwa/routes/external-anchors.js +4 -2
- package/dist/pwa/routes/fee-prepay-requests.js +66 -0
- package/dist/pwa/routes/governance-onboarding.js +46 -8
- package/dist/pwa/routes/logistics.js +4 -4
- package/dist/pwa/routes/me-data.js +2 -2
- package/dist/pwa/routes/mutual-cancel.js +62 -0
- package/dist/pwa/routes/orders-action.js +182 -9
- package/dist/pwa/routes/orders-create.js +18 -7
- package/dist/pwa/routes/orders-read.js +76 -14
- package/dist/pwa/routes/platform-receive-accounts.js +111 -0
- package/dist/pwa/routes/products-create.js +13 -4
- package/dist/pwa/routes/products-update.js +15 -1
- package/dist/pwa/routes/profile-identity.js +4 -2
- package/dist/pwa/routes/returns.js +39 -8
- package/dist/pwa/routes/seller-directpay-report.js +110 -0
- package/dist/pwa/routes/seller-quota.js +5 -1
- package/dist/pwa/routes/shipping-templates.js +219 -0
- package/dist/pwa/routes/snf.js +4 -1
- package/dist/pwa/routes/webauthn.js +3 -3
- package/dist/pwa/server.js +59 -36
- package/dist/runtime/agent-grant-scopes.js +69 -1
- package/dist/runtime/webaz-schema-helpers.js +57 -3
- package/dist/sale-regions.js +116 -0
- package/dist/shipping-templates.js +119 -0
- package/dist/trade-tax.js +99 -0
- package/dist/trade-terms.js +76 -0
- package/dist/version.js +1 -1
- package/package.json +59 -2
|
@@ -784,6 +784,17 @@ export function initArbitratorReviewSchema(db) {
|
|
|
784
784
|
stake_amount INTEGER DEFAULT 0
|
|
785
785
|
)
|
|
786
786
|
`);
|
|
787
|
+
// PR-B 生产仲裁员生命周期字段(ADD COLUMN 必在 CREATE 后 —— schema 铁律)。legacy 行 status NULL 视为 active。
|
|
788
|
+
for (const alter of [
|
|
789
|
+
`ALTER TABLE arbitrator_whitelist ADD COLUMN status TEXT DEFAULT 'active'`, // active | suspended | revoked
|
|
790
|
+
`ALTER TABLE arbitrator_whitelist ADD COLUMN suspended_at TEXT`,
|
|
791
|
+
`ALTER TABLE arbitrator_whitelist ADD COLUMN revoked_at TEXT`,
|
|
792
|
+
]) {
|
|
793
|
+
try {
|
|
794
|
+
db.exec(alter);
|
|
795
|
+
}
|
|
796
|
+
catch { /* 列已存在 */ }
|
|
797
|
+
}
|
|
787
798
|
try {
|
|
788
799
|
db.exec('CREATE INDEX IF NOT EXISTS idx_arb_apps_status ON arbitrator_applications(status)');
|
|
789
800
|
}
|
|
@@ -1220,7 +1231,7 @@ export function initReturnRequestsSchema(db) {
|
|
|
1220
1231
|
reason TEXT NOT NULL, -- 'quality' | 'wrong_item' | 'damaged' | 'no_longer_needed' | 'other'
|
|
1221
1232
|
reason_text TEXT,
|
|
1222
1233
|
refund_amount DECIMAL(18,2), -- 默认 = order.total_amount
|
|
1223
|
-
status TEXT NOT NULL DEFAULT 'pending', -- pending | accepted | rejected | refunded | escalated | cancelled
|
|
1234
|
+
status TEXT NOT NULL DEFAULT 'pending', -- pending | accepted | rejected | refunded | escalated | cancelled | (direct_p2p) await_refund | refund_marked
|
|
1224
1235
|
seller_response TEXT,
|
|
1225
1236
|
escalated_dispute_id TEXT,
|
|
1226
1237
|
created_at TEXT DEFAULT (datetime('now')),
|
|
@@ -1239,6 +1250,15 @@ export function initReturnRequestsSchema(db) {
|
|
|
1239
1250
|
db.exec('CREATE INDEX IF NOT EXISTS idx_returns_order ON return_requests(order_id)');
|
|
1240
1251
|
}
|
|
1241
1252
|
catch { }
|
|
1253
|
+
// 直付(direct_p2p)送达后退货·场外退款握手(src/direct-pay-returns.ts)。ALTER 必须在 CREATE 之后(fresh DB silent-fail 铁律)。
|
|
1254
|
+
try {
|
|
1255
|
+
db.exec('ALTER TABLE return_requests ADD COLUMN refund_reference TEXT');
|
|
1256
|
+
}
|
|
1257
|
+
catch { /* 已存在 */ }
|
|
1258
|
+
try {
|
|
1259
|
+
db.exec('ALTER TABLE return_requests ADD COLUMN await_refund_since TEXT');
|
|
1260
|
+
}
|
|
1261
|
+
catch { /* 已存在 */ }
|
|
1242
1262
|
}
|
|
1243
1263
|
// ─── W2 售后协商时间线(flagged/flag_reasons ALTER 刻意留 server.ts 原位)──
|
|
1244
1264
|
export function initReturnMessagesSchema(db) {
|
|
@@ -1784,13 +1804,47 @@ export function initAgentDelegationGrantsSchema(db) {
|
|
|
1784
1804
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1785
1805
|
expires_at TEXT NOT NULL, -- short-lived (clamped, RFC-020 bearer-first)
|
|
1786
1806
|
revoked_at TEXT,
|
|
1787
|
-
revoked_reason TEXT
|
|
1807
|
+
revoked_reason TEXT,
|
|
1808
|
+
permission_bundle TEXT -- named bundle key if issued/expanded via a bundle (e.g. 'catalog_agent'); NULL = ad-hoc scopes
|
|
1788
1809
|
)
|
|
1789
1810
|
`);
|
|
1811
|
+
try {
|
|
1812
|
+
db.exec(`ALTER TABLE agent_delegation_grants ADD COLUMN permission_bundle TEXT`);
|
|
1813
|
+
}
|
|
1814
|
+
catch { /* existing DB: column already added */ }
|
|
1790
1815
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_adg_human ON agent_delegation_grants(human_id, status)`);
|
|
1791
1816
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_adg_token ON agent_delegation_grants(token_hash)`);
|
|
1792
1817
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_adg_expiry ON agent_delegation_grants(status, expires_at)`);
|
|
1793
1818
|
}
|
|
1819
|
+
/**
|
|
1820
|
+
* RFC-020 — Agent Permission Requests (JIT + bundle authorization). An already-connected agent (holds a
|
|
1821
|
+
* grant) that hits a missing scope, or wants a job bundle, lodges a REQUEST here; the human sees it in
|
|
1822
|
+
* #agent-approvals and approves/rejects. Approval expands the agent's existing grant (safe scopes only,
|
|
1823
|
+
* duration-capped). Bound to (human_id, grant_id). NO money/order/status columns; a request grants nothing
|
|
1824
|
+
* until a human approves it.
|
|
1825
|
+
*/
|
|
1826
|
+
export function initAgentPermissionRequestsSchema(db) {
|
|
1827
|
+
db.exec(`
|
|
1828
|
+
CREATE TABLE IF NOT EXISTS agent_permission_requests (
|
|
1829
|
+
id TEXT PRIMARY KEY, -- apr_xxx
|
|
1830
|
+
human_id TEXT NOT NULL, -- resolved from the requesting agent's grant
|
|
1831
|
+
grant_id TEXT NOT NULL, -- the agent's existing grant that approval expands
|
|
1832
|
+
agent_label TEXT,
|
|
1833
|
+
requested_scopes TEXT NOT NULL DEFAULT '[]', -- JSON string[] — resolved (bundle expanded) safe scopes
|
|
1834
|
+
permission_bundle TEXT, -- bundle key if requested as a bundle; NULL = ad-hoc
|
|
1835
|
+
reason TEXT, -- agent free-text (display only, unverified)
|
|
1836
|
+
task_context TEXT, -- agent free-text about the task (display only)
|
|
1837
|
+
risk_level TEXT NOT NULL DEFAULT 'low', -- low | medium | high (derived, not agent-supplied)
|
|
1838
|
+
duration TEXT NOT NULL DEFAULT '7d', -- once | 1h | 24h | 7d | 30d (capped by risk tier)
|
|
1839
|
+
status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | rejected | expired | revoked
|
|
1840
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1841
|
+
expires_at TEXT NOT NULL, -- request TTL (auto-expire if unanswered)
|
|
1842
|
+
approved_at TEXT
|
|
1843
|
+
)
|
|
1844
|
+
`);
|
|
1845
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_apr_human ON agent_permission_requests(human_id, status)`);
|
|
1846
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_apr_status ON agent_permission_requests(status, expires_at)`);
|
|
1847
|
+
}
|
|
1794
1848
|
/**
|
|
1795
1849
|
* RFC-020 PR-C1 — agent pairing sessions (OAuth device-flow + PKCE shape).
|
|
1796
1850
|
*
|
|
@@ -1812,7 +1866,7 @@ export function initAgentPairingSchema(db) {
|
|
|
1812
1866
|
agent_pubkey TEXT, -- RESERVED (PoP); stored if sent, NOT verified in C1
|
|
1813
1867
|
reason TEXT, -- agent free-text reason (shown in consent)
|
|
1814
1868
|
capabilities TEXT NOT NULL DEFAULT '[]', -- requested SAFE scopes (validated safe-only at start)
|
|
1815
|
-
status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | consumed | expired | revoked
|
|
1869
|
+
status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | consumed | expired | revoked | rejected(human declined)
|
|
1816
1870
|
human_id TEXT, -- set on approve
|
|
1817
1871
|
grant_id TEXT, -- set on approve (issued grant; token_hash filled at retrieve)
|
|
1818
1872
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const REGION_RE = /^[A-Z0-9-]{2,8}$/;
|
|
2
|
+
/** parse-don't-validate:坏 JSON/坏形状 → null(= 不限,fail-open 到原行为;写入侧另有严校验)。 */
|
|
3
|
+
export function parseSaleRegionsRule(raw) {
|
|
4
|
+
if (typeof raw !== 'string' || !raw)
|
|
5
|
+
return null;
|
|
6
|
+
try {
|
|
7
|
+
const r = JSON.parse(raw);
|
|
8
|
+
if (!r || (r.mode !== 'all' && r.mode !== 'list'))
|
|
9
|
+
return null;
|
|
10
|
+
const norm = (a) => Array.isArray(a) ? a.map(x => String(x).toUpperCase()).filter(x => REGION_RE.test(x)) : undefined;
|
|
11
|
+
return { mode: r.mode, include: norm(r.include), exclude: norm(r.exclude) };
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** 写入侧严校验:返回规范化 JSON 串,或 {error}。null/空 = 清除(继承上层)。 */
|
|
18
|
+
export function validateSaleRegionsInput(raw) {
|
|
19
|
+
if (raw === null || raw === undefined || raw === '')
|
|
20
|
+
return { json: null };
|
|
21
|
+
const r = raw;
|
|
22
|
+
if (typeof r !== 'object' || (r.mode !== 'all' && r.mode !== 'list'))
|
|
23
|
+
return { error: "sale_regions.mode 必须是 'all' 或 'list'" };
|
|
24
|
+
const norm = (a, name) => {
|
|
25
|
+
if (a === undefined || a === null)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (!Array.isArray(a) || a.length > 64)
|
|
28
|
+
return { error: `sale_regions.${name} 须为 ≤64 项数组` };
|
|
29
|
+
const out = [];
|
|
30
|
+
for (const x of a) {
|
|
31
|
+
const code = String(x).trim().toUpperCase();
|
|
32
|
+
if (!REGION_RE.test(code))
|
|
33
|
+
return { error: `sale_regions.${name} 含非法地区码:${String(x).slice(0, 12)}(2-8 位大写字母/数字/-)` };
|
|
34
|
+
out.push(code);
|
|
35
|
+
}
|
|
36
|
+
return [...new Set(out)];
|
|
37
|
+
};
|
|
38
|
+
const inc = norm(r.include, 'include');
|
|
39
|
+
if (inc && 'error' in inc)
|
|
40
|
+
return inc;
|
|
41
|
+
const exc = norm(r.exclude, 'exclude');
|
|
42
|
+
if (exc && 'error' in exc)
|
|
43
|
+
return exc;
|
|
44
|
+
if (r.mode === 'list' && (!inc || inc.length === 0))
|
|
45
|
+
return { error: "mode='list' 时 include 不能为空(否则等于全不可卖 —— 想全不可卖请直接下架)" };
|
|
46
|
+
return { json: JSON.stringify({ mode: r.mode, ...(inc ? { include: inc } : {}), ...(exc ? { exclude: exc } : {}) }) };
|
|
47
|
+
}
|
|
48
|
+
/** 生效规则:商品 ?? 店铺(与 accept_mode/运费模板同层级约定)。 */
|
|
49
|
+
export function effectiveSaleRegionsRule(db, product, sellerId) {
|
|
50
|
+
const own = parseSaleRegionsRule(product.sale_regions);
|
|
51
|
+
if (own)
|
|
52
|
+
return own;
|
|
53
|
+
try {
|
|
54
|
+
const row = db.prepare('SELECT store_sale_regions FROM users WHERE id = ?').get(sellerId);
|
|
55
|
+
return parseSaleRegionsRule(row?.store_sale_regions);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 平台合规名单解析(单一真相源:建单门 gateSaleRegionForCreate + S5 预检 shipping-options 共用)。
|
|
63
|
+
* 坏配置(非 JSON / 非数组)→ { ok:false },由调用方 fail-closed(建单 503;预检 sellable=platform_policy_invalid)。
|
|
64
|
+
* 缺省 '[]' → { ok:true, list:[] }。区码统一大写。
|
|
65
|
+
*/
|
|
66
|
+
export function parsePlatformBlocklist(raw) {
|
|
67
|
+
try {
|
|
68
|
+
const p = JSON.parse(String(raw ?? '[]'));
|
|
69
|
+
if (!Array.isArray(p))
|
|
70
|
+
return { ok: false };
|
|
71
|
+
return { ok: true, list: p.map(x => String(x).toUpperCase()) };
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return { ok: false };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export function regionAllowedByRule(rule, region) {
|
|
78
|
+
const r = region.toUpperCase();
|
|
79
|
+
if (rule.exclude?.includes(r))
|
|
80
|
+
return false;
|
|
81
|
+
if (rule.mode === 'list')
|
|
82
|
+
return !!rule.include?.includes(r);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 建单可售门(orders-create 单行调用,两轨共用,运费 gate 之前、任何写之前)。
|
|
87
|
+
* 返回 false = 已写错误响应。平台 overlay 先裁(商家规则不可放宽合规);规则存在但买家没给地区 → fail-closed 要求补选。
|
|
88
|
+
*/
|
|
89
|
+
export function gateSaleRegionForCreate(db, res, product, sellerId, rawRegion, getProtocolParam) {
|
|
90
|
+
const region = (typeof rawRegion === 'string' && rawRegion.trim()) ? rawRegion.trim().toUpperCase() : null;
|
|
91
|
+
// ① 平台合规 overlay(protocol param,DEFAULT_PARAMS 已 seed '[]';admin PATCH 有 JSON+区码校验)。
|
|
92
|
+
// 坏配置【fail-closed】:合规名单读不懂时放行 = 静默解除平台禁售(审计 P2) —— 宁可挡单也不裸奔;
|
|
93
|
+
// admin 写入侧已强校验,坏值只可能来自手改 DB,错误信息直接指路。
|
|
94
|
+
const parsedBlock = parsePlatformBlocklist(getProtocolParam('trade.platform_region_blocklist', '[]'));
|
|
95
|
+
if (!parsedBlock.ok) {
|
|
96
|
+
res.status(503).json({ error: '平台合规配置异常,暂无法下单(运营侧需修复 trade.platform_region_blocklist 参数)', error_code: 'PLATFORM_REGION_POLICY_INVALID' });
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const platformBlock = parsedBlock.list;
|
|
100
|
+
const rule = effectiveSaleRegionsRule(db, product, sellerId);
|
|
101
|
+
if (platformBlock.length === 0 && !rule)
|
|
102
|
+
return true; // 无任何限制 = 原行为(地区可选)
|
|
103
|
+
if (!region) {
|
|
104
|
+
res.status(400).json({ error: '该商品设有可售地区限制,请选择收货国家/地区', error_code: 'SHIP_REGION_REQUIRED' });
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (platformBlock.includes(region)) {
|
|
108
|
+
res.status(409).json({ error: '平台合规限制:该商品暂不支持销售到该地区', error_code: 'PRODUCT_RESTRICTED', region });
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (rule && !regionAllowedByRule(rule, region)) {
|
|
112
|
+
res.status(409).json({ error: '卖家未将该地区设为可售范围(与运费无关,不适用询价)', error_code: 'REGION_NOT_FOR_SALE', region });
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { toUnits } from './money.js';
|
|
2
|
+
import { freeShippingWaives } from './free-shipping.js'; // 营销域:满额免邮(S2 返工 —— 促销语义不进成本模板)
|
|
3
|
+
const MAX_ENTRIES = 50;
|
|
4
|
+
const MAX_REGION_LEN = 16;
|
|
5
|
+
const MAX_EST_LEN = 20;
|
|
6
|
+
const MAX_FEE = 1_000_000;
|
|
7
|
+
/** 规范化地区 code:trim + 大写。空/非串 → null。 */
|
|
8
|
+
export function normalizeRegion(raw) {
|
|
9
|
+
if (typeof raw !== 'string')
|
|
10
|
+
return null;
|
|
11
|
+
const r = raw.trim().toUpperCase();
|
|
12
|
+
if (!r || r.length > MAX_REGION_LEN)
|
|
13
|
+
return null;
|
|
14
|
+
return r;
|
|
15
|
+
}
|
|
16
|
+
/** 校验并规范化模板。null/undefined = 清除模板(合法)。返回 {ok, entries} 或 {ok:false, error}。 */
|
|
17
|
+
export function parseShippingTemplate(raw) {
|
|
18
|
+
if (raw == null)
|
|
19
|
+
return { ok: true, entries: null };
|
|
20
|
+
if (!Array.isArray(raw))
|
|
21
|
+
return { ok: false, error: '模板必须是数组' };
|
|
22
|
+
if (raw.length === 0)
|
|
23
|
+
return { ok: true, entries: null }; // 空数组视同清除
|
|
24
|
+
if (raw.length > MAX_ENTRIES)
|
|
25
|
+
return { ok: false, error: `模板最多 ${MAX_ENTRIES} 条` };
|
|
26
|
+
const out = [];
|
|
27
|
+
const seen = new Set();
|
|
28
|
+
for (const e of raw) {
|
|
29
|
+
const region = e && e.region === '*' ? '*' : normalizeRegion(e?.region);
|
|
30
|
+
if (!region)
|
|
31
|
+
return { ok: false, error: '每条须有合法 region(≤16 字大写 code 或 *)' };
|
|
32
|
+
if (seen.has(region))
|
|
33
|
+
return { ok: false, error: `region 重复:${region}` };
|
|
34
|
+
seen.add(region);
|
|
35
|
+
const fee = Number(e?.fee);
|
|
36
|
+
if (!Number.isFinite(fee) || fee < 0 || fee > MAX_FEE)
|
|
37
|
+
return { ok: false, error: `运费必须是 0~${MAX_FEE} 的数字(${region})` };
|
|
38
|
+
const est = e?.est_days == null ? undefined : String(e.est_days).trim().slice(0, MAX_EST_LEN);
|
|
39
|
+
out.push({ region, fee: Math.round(fee * 100) / 100, ...(est ? { est_days: est } : {}) });
|
|
40
|
+
}
|
|
41
|
+
return { ok: true, entries: out };
|
|
42
|
+
}
|
|
43
|
+
/** 解析已存 JSON(容错:坏 JSON/坏结构 → null=无模板,不阻断读路径)。 */
|
|
44
|
+
export function loadTemplateJson(json) {
|
|
45
|
+
if (!json)
|
|
46
|
+
return null;
|
|
47
|
+
try {
|
|
48
|
+
const p = parseShippingTemplate(JSON.parse(json));
|
|
49
|
+
return p.ok ? p.entries : null;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** 生效模板:单品覆盖 ?? 店铺默认 ?? null。product 传订单路径已取到的行(含 shipping_template 列)。 */
|
|
56
|
+
export function effectiveShippingTemplate(db, product, sellerId) {
|
|
57
|
+
const own = loadTemplateJson(product.shipping_template);
|
|
58
|
+
if (own)
|
|
59
|
+
return own;
|
|
60
|
+
try {
|
|
61
|
+
const row = db.prepare('SELECT store_shipping_template FROM users WHERE id = ?').get(sellerId);
|
|
62
|
+
return loadTemplateJson(row?.store_shipping_template);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** 按买家地区解析运费:精确 → '*' → 未覆盖。(纯成本结构;营销免邮在 gate 层应用) */
|
|
69
|
+
export function resolveShipping(entries, region) {
|
|
70
|
+
const hit = entries.find(e => e.region === region) ?? entries.find(e => e.region === '*');
|
|
71
|
+
if (!hit)
|
|
72
|
+
return { covered: false, fee: 0, est_days: null, matched: null };
|
|
73
|
+
return { covered: true, fee: hit.fee, est_days: hit.est_days ?? null, matched: hit.region === region ? 'exact' : 'wildcard' };
|
|
74
|
+
}
|
|
75
|
+
/** 询价 opt-in(PR-3):单品 shipping_quote_ok ?? 店铺 store_shipping_quote_ok ?? 关。 */
|
|
76
|
+
export function quoteOutsideTemplateOk(db, product, sellerId) {
|
|
77
|
+
if (product.shipping_quote_ok != null)
|
|
78
|
+
return Number(product.shipping_quote_ok) === 1;
|
|
79
|
+
try {
|
|
80
|
+
const row = db.prepare('SELECT store_shipping_quote_ok FROM users WHERE id = ?').get(sellerId);
|
|
81
|
+
return Number(row?.store_shipping_quote_ok) === 1;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 建单运费守门(orders-create 单行调用,两轨共用,任何 DB write 之前):
|
|
89
|
+
* - 无模板:不要求地区(给了就规范化快照),运费 0 —— 原行为。
|
|
90
|
+
* - 有模板:必须给合法 ship_to_region(否则 400 SHIP_REGION_REQUIRED);命中 → 返回运费(调用方并入总额)。
|
|
91
|
+
* - 未命中(含无 '*'):卖家开了询价(quoteOutsideTemplateOk)且直付轨 → quoteRequired=true(建单落
|
|
92
|
+
* pending_accept,卖家先报运费/时效、买家确认新总额才进付款窗 —— PR-3);否则(未开询价 / escrow 轨,
|
|
93
|
+
* escrow 询价需付款后置钱路手术,PR-5)→ 409 SHIP_REGION_NOT_COVERED。
|
|
94
|
+
* 返回 null = 已写错误响应,调用方直接 return。
|
|
95
|
+
*/
|
|
96
|
+
export function gateShippingForCreate(db, res, product, sellerId, rawRegion, rail = 'escrow', goodsSubtotalU) {
|
|
97
|
+
const region = normalizeRegion(rawRegion);
|
|
98
|
+
const tpl = effectiveShippingTemplate(db, product, sellerId);
|
|
99
|
+
if (!tpl)
|
|
100
|
+
return { feeU: 0, fee: 0, region, estDays: null, quoteRequired: false };
|
|
101
|
+
if (!region) {
|
|
102
|
+
res.status(400).json({ error: '该商品按地区计运费,请选择收货国家/地区', error_code: 'SHIP_REGION_REQUIRED' });
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const r = resolveShipping(tpl, region);
|
|
106
|
+
if (r.covered) {
|
|
107
|
+
// 营销域满额免邮(S2 返工:规则在 free-shipping.ts,不在模板)—— 模板费为正 + 券后货款达标 → 免为 0。
|
|
108
|
+
// 人工询价路径不经此分支(quoteRequired 在下方),天然豁免。
|
|
109
|
+
if (r.fee > 0 && goodsSubtotalU !== undefined && freeShippingWaives(db, product, sellerId, goodsSubtotalU)) {
|
|
110
|
+
return { feeU: 0, fee: 0, region, estDays: r.est_days, quoteRequired: false, freeThresholdApplied: true };
|
|
111
|
+
}
|
|
112
|
+
return { feeU: toUnits(r.fee), fee: r.fee, region, estDays: r.est_days, quoteRequired: false };
|
|
113
|
+
}
|
|
114
|
+
if (rail === 'direct_p2p' && quoteOutsideTemplateOk(db, product, sellerId)) {
|
|
115
|
+
return { feeU: 0, fee: 0, region, estDays: null, quoteRequired: true }; // 运费待卖家报价,先不入总额
|
|
116
|
+
}
|
|
117
|
+
res.status(409).json({ error: rail === 'direct_p2p' ? '卖家暂不配送到该地区' : '卖家暂不配送到该地区(该商品支持直付询价的话,可改用直付下单)', error_code: 'SHIP_REGION_NOT_COVERED', region });
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨境税费/进口责任【声明+披露】层(S3)—— 卖家 seller-of-record 自我声明,平台【绝不计算/代收/代缴】。
|
|
3
|
+
*
|
|
4
|
+
* 定位纪律(调研结论,2026-07):非托管≠必然免平台义务。EU Art.14a "deemed supplier" 的免责是三肢连接测试
|
|
5
|
+
* (不设条款 且 不授权收款 且 不涉订单/配送),WebAZ 设条款+涉订单 → 极可能被认定;US CA/WA 型 "any-activity"
|
|
6
|
+
* 测试里"传递要约/承诺+接单+上架"各自独立触发,与是否碰钱无关。故本层【只声明只披露】,不碰税款:
|
|
7
|
+
* - import_duty_terms(DDP/DDU):卖家声明谁承担进口关税/税(DDP=卖家已含于价;DDU=买家到境自付)。
|
|
8
|
+
* - tax_lines(仅 'included' 类):卖家声明"价内已含 X 税"(如 SG GST 9%)—— 纯信息披露,不改总额、不入钱路。
|
|
9
|
+
* 刻意【不做】(不上假开关):'added' 类税进总额(钱路,单独 PR 门控真实需求)、自动税率、代收代缴(Rail 3+律师)。
|
|
10
|
+
* 合规义务(即便非 deemed):EU 10 年 / UK 6 年交易留痕由 orders.trade_terms_snapshot 满足(S0 已冻结卖家/货/值/时地/单号)。
|
|
11
|
+
* 跨境进高执法辖区(EU/UK 及 CA/WA 型州)的 deemed-supplier 姿态 → 由 S1 的 trade.platform_region_blocklist 治理门
|
|
12
|
+
* + 律师确认逐区放行(本层不自动放开)。详 docs/COMPLIANCE-CROSS-BORDER-TAX.INTERNAL.md。
|
|
13
|
+
*
|
|
14
|
+
* 层级:products.{import_duty_terms,tax_lines} ?? users.store_{import_duty_terms,tax_lines}(与接单/运费/可售同约定)。
|
|
15
|
+
* 快照:S0 的 trade-terms declarations.{import_duty_terms,tax_lines} 已从这些列自动填充 —— 本模块只管写入校验+生效解析+披露。
|
|
16
|
+
*/
|
|
17
|
+
const REGION_RE = /^[A-Z0-9-]{2,8}$/;
|
|
18
|
+
/** DDP/DDU 写入校验:null/空=清除(继承上层);'ddu'|'ddp' 合法。 */
|
|
19
|
+
export function validateImportDutyTerms(raw) {
|
|
20
|
+
if (raw === null || raw === undefined || raw === '')
|
|
21
|
+
return { value: null };
|
|
22
|
+
if (raw === 'ddu' || raw === 'ddp')
|
|
23
|
+
return { value: raw };
|
|
24
|
+
return { error: "import_duty_terms 必须是 'ddu'(买家到境自付)或 'ddp'(卖家已含)或 null" };
|
|
25
|
+
}
|
|
26
|
+
/** 税费科目写入校验:S3 只收 'included'(价内已含,纯披露)。'added'(进总额)明确拒 —— 钱路未开,不上假开关。 */
|
|
27
|
+
export function validateTaxLines(raw) {
|
|
28
|
+
if (raw === null || raw === undefined || raw === '')
|
|
29
|
+
return { value: null };
|
|
30
|
+
if (!Array.isArray(raw))
|
|
31
|
+
return { error: 'tax_lines 必须是数组或 null' };
|
|
32
|
+
if (raw.length > 32)
|
|
33
|
+
return { error: 'tax_lines 最多 32 条' };
|
|
34
|
+
const out = [];
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
for (const e of raw) {
|
|
37
|
+
if (!e || typeof e !== 'object')
|
|
38
|
+
return { error: 'tax_lines 每条须是对象' };
|
|
39
|
+
const r = e;
|
|
40
|
+
if (r.kind !== undefined && r.kind !== 'included') { // 省略 kind = 'included'(S3 唯一允许值);未来开 'added' 钱路时须显式,勿继承此静默默认
|
|
41
|
+
return { error: r.kind === 'added' ? "'added' 税费(进总额)暂不支持 —— 平台不代收税;仅支持 'included'(价内已含,披露)" : "tax_lines.kind 仅支持 'included'" };
|
|
42
|
+
}
|
|
43
|
+
const region = r.region === '*' ? '*' : (typeof r.region === 'string' ? r.region.trim().toUpperCase() : '');
|
|
44
|
+
if (region !== '*' && !REGION_RE.test(region))
|
|
45
|
+
return { error: `tax_lines 含非法 region:${String(r.region).slice(0, 12)}(2-8 位大写码或 *)` };
|
|
46
|
+
if (seen.has(region))
|
|
47
|
+
return { error: `tax_lines region 重复:${region}` };
|
|
48
|
+
seen.add(region);
|
|
49
|
+
const label = typeof r.label === 'string' ? r.label.trim().slice(0, 40) : '';
|
|
50
|
+
if (!label)
|
|
51
|
+
return { error: `tax_lines 每条须有 label(≤40 字,如 GST / VAT)(${region})` };
|
|
52
|
+
let rate_pct;
|
|
53
|
+
if (r.rate_pct !== undefined && r.rate_pct !== null) {
|
|
54
|
+
if (typeof r.rate_pct !== 'number' && typeof r.rate_pct !== 'string')
|
|
55
|
+
return { error: `tax_lines.rate_pct 必须是数字(${region})` }; // 审计:拒 Number(true)=1 类强转
|
|
56
|
+
const n = Number(r.rate_pct);
|
|
57
|
+
if (!Number.isFinite(n) || n < 0 || n > 100)
|
|
58
|
+
return { error: `tax_lines.rate_pct 必须是 0~100(${region})` };
|
|
59
|
+
rate_pct = Math.round(n * 100) / 100;
|
|
60
|
+
}
|
|
61
|
+
const note = typeof r.note === 'string' && r.note.trim() ? r.note.trim().slice(0, 80) : undefined;
|
|
62
|
+
out.push({ region, label, ...(rate_pct !== undefined ? { rate_pct } : {}), ...(note ? { note } : {}), kind: 'included' });
|
|
63
|
+
}
|
|
64
|
+
return { value: JSON.stringify(out) };
|
|
65
|
+
}
|
|
66
|
+
/** parse-don't-validate 读:坏 JSON/坏形状 → null。 */
|
|
67
|
+
export function parseTaxLines(raw) {
|
|
68
|
+
if (typeof raw !== 'string' || !raw)
|
|
69
|
+
return null;
|
|
70
|
+
try {
|
|
71
|
+
const a = JSON.parse(raw);
|
|
72
|
+
if (!Array.isArray(a))
|
|
73
|
+
return null;
|
|
74
|
+
return a.filter((e) => !!e && typeof e === 'object' && typeof e.region === 'string' && typeof e.label === 'string');
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** 生效 DDP/DDU:商品 ?? 店铺 ?? null(纯函数;product/store 值由调用方各自的 DB seam 取好传入 —— 消费方是 async seam 路由,不持 sync db)。 */
|
|
81
|
+
export function effectiveImportDutyTerms(productVal, storeVal) {
|
|
82
|
+
const norm = (x) => (x === 'ddu' || x === 'ddp') ? x : null;
|
|
83
|
+
return norm(productVal) ?? norm(storeVal);
|
|
84
|
+
}
|
|
85
|
+
/** 生效税费科目:商品 ?? 店铺 ?? null。 */
|
|
86
|
+
export function effectiveTaxLines(productVal, storeVal) {
|
|
87
|
+
const own = parseTaxLines(productVal);
|
|
88
|
+
if (own && own.length)
|
|
89
|
+
return own;
|
|
90
|
+
const st = parseTaxLines(storeVal);
|
|
91
|
+
return (st && st.length) ? st : null;
|
|
92
|
+
}
|
|
93
|
+
/** 按目的区筛生效税费(精确 region → '*' 兜底,合并;披露用)。 */
|
|
94
|
+
export function taxLinesForRegion(lines, region) {
|
|
95
|
+
if (!lines)
|
|
96
|
+
return [];
|
|
97
|
+
const r = (region || '').toUpperCase();
|
|
98
|
+
return lines.filter(l => l.region === '*' || (r && l.region === r));
|
|
99
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** 组装快照(读商品行 + 店铺兜底;shipping 由建单 gate 结果传入 —— 不重算,记录裁决结果)。 */
|
|
2
|
+
export function buildTradeTermsSnapshot(db, args) {
|
|
3
|
+
// fail-soft 读:缺列(裸测试库/schema 漂移)→ 降级为空槽快照,绝不让证据采集炸掉建单
|
|
4
|
+
let p;
|
|
5
|
+
let u;
|
|
6
|
+
try {
|
|
7
|
+
p = db.prepare(`SELECT handling_hours, estimated_days, return_days, return_condition, warranty_days,
|
|
8
|
+
weight_kg, package_size, origin_country, country_of_origin, customs_description, hs_code,
|
|
9
|
+
ship_regions, sale_regions, tax_lines, import_duty_terms FROM products WHERE id = ?`).get(args.productId);
|
|
10
|
+
u = db.prepare('SELECT store_sale_regions, store_tax_lines, store_import_duty_terms FROM users WHERE id = ?')
|
|
11
|
+
.get(args.sellerId);
|
|
12
|
+
}
|
|
13
|
+
catch { /* 空槽降级 */ }
|
|
14
|
+
const parse = (x) => { if (typeof x !== 'string' || !x)
|
|
15
|
+
return null; try {
|
|
16
|
+
return JSON.parse(x);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
} };
|
|
21
|
+
const numOrNull = (x) => (x === null || x === undefined || x === '' ? null : Number(x));
|
|
22
|
+
const strOrNull = (x) => (typeof x === 'string' && x !== '' ? x : null);
|
|
23
|
+
const duty = strOrNull(p?.import_duty_terms) ?? strOrNull(u?.store_import_duty_terms);
|
|
24
|
+
return {
|
|
25
|
+
v: 1,
|
|
26
|
+
captured_at: new Date().toISOString(),
|
|
27
|
+
shipping: { source: args.shipping.source, region: args.shipping.region, fee: args.shipping.fee, est_days: args.shipping.estDays, ...(args.shipping.freeThresholdApplied ? { free_threshold_applied: true } : {}) },
|
|
28
|
+
fulfilment: {
|
|
29
|
+
handling_hours: numOrNull(p?.handling_hours), estimated_days: strOrNull(p?.estimated_days),
|
|
30
|
+
return_days: numOrNull(p?.return_days), return_condition: strOrNull(p?.return_condition), warranty_days: numOrNull(p?.warranty_days),
|
|
31
|
+
},
|
|
32
|
+
logistics: {
|
|
33
|
+
weight_kg: numOrNull(p?.weight_kg), package_size: strOrNull(p?.package_size),
|
|
34
|
+
origin_country: strOrNull(p?.origin_country), country_of_origin: strOrNull(p?.country_of_origin),
|
|
35
|
+
customs_description: strOrNull(p?.customs_description), hs_code: strOrNull(p?.hs_code),
|
|
36
|
+
},
|
|
37
|
+
declarations: {
|
|
38
|
+
ship_regions_text: strOrNull(p?.ship_regions),
|
|
39
|
+
sale_regions_rule: parse(p?.sale_regions) ?? parse(u?.store_sale_regions),
|
|
40
|
+
tax_lines: parse(p?.tax_lines) ?? parse(u?.store_tax_lines),
|
|
41
|
+
import_duty_terms: duty === 'ddu' || duty === 'ddp' ? duty : null,
|
|
42
|
+
},
|
|
43
|
+
accept_mode: args.acceptModeEffective,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** 写快照(fail-soft:任何异常吞掉不阻断建单 —— 快照缺失=pre-S0 订单同待遇,消费方须容错)。 */
|
|
47
|
+
export function writeTradeTermsSnapshot(db, orderId, snap) {
|
|
48
|
+
try {
|
|
49
|
+
db.prepare('UPDATE orders SET trade_terms_snapshot = ? WHERE id = ?').run(JSON.stringify(snap), orderId);
|
|
50
|
+
}
|
|
51
|
+
catch { /* fail-soft */ }
|
|
52
|
+
}
|
|
53
|
+
/** 询价确认后补记运费裁决(quote_pending → quote;只动 shipping 槽,其余条款保持下单时冻结值)。 */
|
|
54
|
+
export function updateSnapshotShippingQuote(db, orderId, fee, estDays) {
|
|
55
|
+
try {
|
|
56
|
+
const row = db.prepare('SELECT trade_terms_snapshot FROM orders WHERE id = ?').get(orderId);
|
|
57
|
+
if (!row?.trade_terms_snapshot)
|
|
58
|
+
return;
|
|
59
|
+
const snap = JSON.parse(row.trade_terms_snapshot);
|
|
60
|
+
snap.shipping = { ...snap.shipping, source: 'quote', fee, est_days: estDays };
|
|
61
|
+
db.prepare('UPDATE orders SET trade_terms_snapshot = ? WHERE id = ?').run(JSON.stringify(snap), orderId);
|
|
62
|
+
}
|
|
63
|
+
catch { /* fail-soft */ }
|
|
64
|
+
}
|
|
65
|
+
/** parse-don't-validate 读取(DTO/争议消费方用;坏 JSON/缺失 → null)。 */
|
|
66
|
+
export function readTradeTermsSnapshot(raw) {
|
|
67
|
+
if (typeof raw !== 'string' || !raw)
|
|
68
|
+
return null;
|
|
69
|
+
try {
|
|
70
|
+
const s = JSON.parse(raw);
|
|
71
|
+
return s && s.v === 1 ? s : null;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
package/dist/version.js
CHANGED
|
@@ -31,4 +31,4 @@ export const SOFTWARE_VERSION = pkg.version;
|
|
|
31
31
|
* safe for agents to ignore. Guarded by tests/test-contract-fingerprint.ts + docs/CONTRACT-LOCK.json.
|
|
32
32
|
* NB: the fingerprint hashes only §②/§① content; a change to the §④ entry document (e.g. agent_quickstart)
|
|
33
33
|
* is integrator-observable but NOT fingerprinted, so it must be registered here by hand. */
|
|
34
|
-
export const CONTRACT_VERSION =
|
|
34
|
+
export const CONTRACT_VERSION = 23;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seasonkoh/webaz",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "[PRE-LAUNCH] Agent-native decentralized commerce protocol. Humans and AI agents trade on the same protocol via MCP tools. ⚠️ Repository currently private until W8 public launch — GitHub links may return 404. See https://webaz.xyz for status.",
|
|
5
5
|
"main": "dist/mcp.js",
|
|
6
6
|
"bin": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"meta-rules:check": "tsx scripts/meta-rules-invariant-check.ts",
|
|
40
40
|
"params:check": "tsx scripts/meta-rule-locked-params-check.ts",
|
|
41
41
|
"preflight": "tsx scripts/preflight.ts",
|
|
42
|
+
"preflight:push": "npm run check:api-docs-fresh && npm run contract:verify && npm run schema:verify && npm run pg:verify && npm run guard:complexity && npm run guard:pr-constraints && npm run routes:seam-check && npm run check:pwa-syntax && npm run build && echo '✅ preflight:push all green'",
|
|
42
43
|
"test:github-credential": "tsx scripts/test-github-credential.ts",
|
|
43
44
|
"test:github-fetch-adapter": "tsx scripts/test-github-fetch-adapter.ts",
|
|
44
45
|
"test:github-authenticated-read": "tsx scripts/test-github-authenticated-read.ts",
|
|
@@ -82,6 +83,11 @@
|
|
|
82
83
|
"test:mcp-fresh-db-schema": "tsx scripts/test-mcp-fresh-db-schema.ts",
|
|
83
84
|
"test:agent-delegation-grants": "tsx scripts/test-agent-delegation-grants.ts",
|
|
84
85
|
"test:agent-pairing": "tsx scripts/test-agent-pairing.ts",
|
|
86
|
+
"test:agent-pair-ui": "tsx scripts/test-agent-pair-ui.ts",
|
|
87
|
+
"test:agent-perm-taxonomy": "tsx scripts/test-agent-perm-taxonomy.ts",
|
|
88
|
+
"test:agent-permission-requests": "tsx scripts/test-agent-permission-requests.ts",
|
|
89
|
+
"test:agent-permission-consume": "tsx scripts/test-agent-permission-consume.ts",
|
|
90
|
+
"test:agent-approvals-ui": "tsx scripts/test-agent-approvals-ui.ts",
|
|
85
91
|
"test:agent-grant-verify": "tsx scripts/test-agent-grant-verify.ts",
|
|
86
92
|
"test:agent-grant-mcp-consume": "tsx scripts/test-agent-grant-mcp-consume.ts",
|
|
87
93
|
"test:connected-agents-read": "tsx scripts/test-connected-agents-read.ts",
|
|
@@ -99,7 +105,7 @@
|
|
|
99
105
|
"test:currency-display": "tsx scripts/test-currency-display.ts",
|
|
100
106
|
"test:currency-schema-flip": "tsx scripts/test-currency-schema-flip.ts",
|
|
101
107
|
"test:proposal-admin-ui": "tsx scripts/test-proposal-admin-ui.ts",
|
|
102
|
-
"check:pwa-syntax": "node --check src/pwa/public/app.js && node --check src/pwa/public/app-admin.js && node --check src/pwa/public/app-contribution.js && node --check src/pwa/public/app-ai.js && node --check src/pwa/public/app-discover.js && node --check src/pwa/public/app-profile.js && node --check src/pwa/public/app-account.js && node --check src/pwa/public/app-shop.js && node --check src/pwa/public/app-listings.js && node --check src/pwa/public/app-external-links.js && node --check src/pwa/public/app-product-media.js && node --check src/pwa/public/app-create-kinds.js && node --check src/pwa/public/app-price.js && node --check src/pwa/public/app-seller.js && node --check src/pwa/public/app-agents.js && node --check src/pwa/public/app-direct-pay.js && node --check src/pwa/public/app-direct-pay-readiness.js && node --check src/pwa/public/app-direct-pay-deferral.js && node --check src/pwa/public/app-direct-pay-deferral-admin.js && node --check src/pwa/public/app-direct-pay-product-verify.js && node --check src/pwa/public/app-direct-pay-store-verify.js && node --check src/pwa/public/app-direct-pay-compliance.js && node --check src/pwa/public/app-direct-pay-fee-ops.js && node --check src/pwa/public/app-direct-pay-fee-center.js && node --check src/pwa/public/app-direct-pay-accounts.js && node --check src/pwa/public/app-direct-pay-buyer.js && node --check src/pwa/public/app-prelaunch-waz.js && node --check src/pwa/public/app-chat-poll.js && node --check src/pwa/public/i18n.js && node --check src/pwa/public/sw.js",
|
|
108
|
+
"check:pwa-syntax": "node --check src/pwa/public/app-notif-templates.js && node --check src/pwa/public/app-notif-templates-orders.js && node --check src/pwa/public/app.js && node --check src/pwa/public/app-admin.js && node --check src/pwa/public/app-admin-disputes.js && node --check src/pwa/public/app-contribution.js && node --check src/pwa/public/app-ai.js && node --check src/pwa/public/app-discover.js && node --check src/pwa/public/app-profile.js && node --check src/pwa/public/app-account.js && node --check src/pwa/public/app-shop.js && node --check src/pwa/public/app-listings.js && node --check src/pwa/public/app-external-links.js && node --check src/pwa/public/app-product-media.js && node --check src/pwa/public/app-create-kinds.js && node --check src/pwa/public/app-price.js && node --check src/pwa/public/app-order-labels.js && node --check src/pwa/public/app-seller.js && node --check src/pwa/public/app-agents.js && node --check src/pwa/public/app-agent-pair.js && node --check src/pwa/public/app-agent-approvals.js && node --check src/pwa/public/app-agent-appeal.js && node --check src/pwa/public/app-direct-pay.js && node --check src/pwa/public/app-direct-pay-readiness.js && node --check src/pwa/public/app-direct-pay-deferral.js && node --check src/pwa/public/app-direct-pay-deferral-admin.js && node --check src/pwa/public/app-direct-pay-product-verify.js && node --check src/pwa/public/app-direct-pay-store-verify.js && node --check src/pwa/public/app-direct-pay-compliance.js && node --check src/pwa/public/app-direct-pay-fee-ops.js && node --check src/pwa/public/app-direct-pay-fee-center.js && node --check src/pwa/public/app-direct-pay-sales-report.js && node --check src/pwa/public/app-gmv-rail-split.js && node --check src/pwa/public/app-direct-pay-accounts.js && node --check src/pwa/public/app-direct-pay-buyer.js && node --check src/pwa/public/app-direct-pay-pay.js && node --check src/pwa/public/app-direct-pay-reveal.js && node --check src/pwa/public/app-direct-pay-copy.js && node --check src/pwa/public/app-direct-pay-paymodal.js && node --check src/pwa/public/app-direct-pay-memo.js && node --check src/pwa/public/app-direct-pay-negotiation.js && node --check src/pwa/public/app-direct-pay-cancel-refund.js && node --check src/pwa/public/app-direct-pay-returns.js && node --check src/pwa/public/app-order-accept-ui.js && node --check src/pwa/public/app-order-rail-filter.js && node --check src/pwa/public/app-sale-regions-ui.js && node --check src/pwa/public/app-free-shipping-ui.js && node --check src/pwa/public/app-trade-tax-ui.js && node --check src/pwa/public/app-purchase-terms-ui.js && node --check src/pwa/public/app-listing-commerce-ui.js && node --check src/pwa/public/app-bond-terms-ui.js && node --check src/pwa/public/app-bond-ui.js && node --check src/pwa/public/app-bond-refund-ui.js && node --check src/pwa/public/app-bond-slash-ui.js && node --check src/pwa/public/app-bond-deferral-ui.js && node --check src/pwa/public/app-direct-pay-reconcile.js && node --check src/pwa/public/app-mutual-cancel.js && node --check src/pwa/public/app-dispute-close-ui.js && node --check src/pwa/public/app-order-errors.js && node --check src/pwa/public/app-arbitrator-entry.js && node --check src/pwa/public/app-arbitrator-admin.js && node --check src/pwa/public/app-contribution-hub.js && node --check src/pwa/public/app-platform-receive-accounts.js && node --check src/pwa/public/app-direct-pay-fee-request.js && node --check src/pwa/public/app-direct-pay-fee-requests-admin.js && node --check src/pwa/public/app-direct-pay-fee-history.js && node --check src/pwa/public/app-prelaunch-waz.js && node --check src/pwa/public/app-chat-poll.js && node --check src/pwa/public/app-poll-governor.js && node --check src/pwa/public/i18n.js && node --check src/pwa/public/sw.js",
|
|
103
109
|
"test:create-kinds": "tsx scripts/test-create-kinds.ts",
|
|
104
110
|
"test:secondhand-photo-authenticity": "tsx scripts/test-secondhand-photo-authenticity.ts",
|
|
105
111
|
"test:secondhand-detail-alignment": "tsx scripts/test-secondhand-detail-alignment.ts",
|
|
@@ -123,6 +129,9 @@
|
|
|
123
129
|
"test:direct-receive-deposits": "tsx scripts/test-direct-receive-deposits.ts",
|
|
124
130
|
"test:direct-receive-accounts": "tsx scripts/test-direct-receive-accounts.ts",
|
|
125
131
|
"test:direct-receive-account-endpoints": "tsx scripts/test-direct-receive-account-endpoints.ts",
|
|
132
|
+
"test:platform-receive-accounts": "tsx scripts/test-platform-receive-accounts.ts",
|
|
133
|
+
"test:fee-prepay-request": "tsx scripts/test-fee-prepay-request.ts",
|
|
134
|
+
"test:fee-prepay-review": "tsx scripts/test-fee-prepay-review.ts",
|
|
126
135
|
"test:direct-pay-order-qr": "tsx scripts/test-direct-pay-order-qr.ts",
|
|
127
136
|
"test:direct-pay-order-redaction": "tsx scripts/test-direct-pay-order-redaction.ts",
|
|
128
137
|
"guard:direct-pay-order-reader": "tsx scripts/direct-pay-order-reader-guard.ts",
|
|
@@ -132,6 +141,40 @@
|
|
|
132
141
|
"test:direct-pay-base-bond-entry": "tsx scripts/test-direct-pay-base-bond-entry.ts",
|
|
133
142
|
"test:direct-pay-order-snapshot-schema": "tsx scripts/test-direct-pay-order-snapshot-schema.ts",
|
|
134
143
|
"test:direct-pay-ui": "tsx scripts/test-direct-pay-ui.ts",
|
|
144
|
+
"test:order-errors": "tsx scripts/test-order-errors.ts",
|
|
145
|
+
"test:fee-request-history": "tsx scripts/test-fee-request-history.ts",
|
|
146
|
+
"test:i18n-parity": "tsx scripts/test-i18n-parity.ts",
|
|
147
|
+
"test:arbitrator-lifecycle": "tsx scripts/test-arbitrator-lifecycle.ts",
|
|
148
|
+
"test:arbitrator-admin-routes": "tsx scripts/test-arbitrator-admin-routes.ts",
|
|
149
|
+
"test:arbitrate-human-presence": "tsx scripts/test-arbitrate-human-presence.ts",
|
|
150
|
+
"test:arbitrate-route-e2e": "tsx scripts/test-arbitrate-route-e2e.ts",
|
|
151
|
+
"test:dispute-dismiss-negotiation": "tsx scripts/test-dispute-dismiss-negotiation.ts",
|
|
152
|
+
"test:disputes-read-auth": "tsx scripts/test-disputes-read-auth.ts",
|
|
153
|
+
"test:admin-dispute-triage": "tsx scripts/test-admin-dispute-triage.ts",
|
|
154
|
+
"test:orders-read-arbitrator-view": "tsx scripts/test-orders-read-arbitrator-view.ts",
|
|
155
|
+
"test:orders-rail-filter": "tsx scripts/test-orders-rail-filter.ts",
|
|
156
|
+
"test:directpay-sales-report": "tsx scripts/test-directpay-sales-report.ts",
|
|
157
|
+
"test:dashboard-gmv-by-rail": "tsx scripts/test-dashboard-gmv-by-rail.ts",
|
|
158
|
+
"test:direct-pay-cancel-refund": "tsx scripts/test-direct-pay-cancel-refund.ts",
|
|
159
|
+
"test:direct-pay-returns": "tsx scripts/test-direct-pay-returns.ts",
|
|
160
|
+
"test:direct-pay-pending-accept": "tsx scripts/test-direct-pay-pending-accept.ts",
|
|
161
|
+
"test:shipping-templates": "tsx scripts/test-shipping-templates.ts",
|
|
162
|
+
"test:shipping-quote": "tsx scripts/test-shipping-quote.ts",
|
|
163
|
+
"test:order-accept-ui": "tsx scripts/test-order-accept-ui.ts",
|
|
164
|
+
"test:bond-deposit-flow": "tsx scripts/test-bond-deposit-flow.ts",
|
|
165
|
+
"test:bond-refund-flow": "tsx scripts/test-bond-refund-flow.ts",
|
|
166
|
+
"test:bond-slash-flow": "tsx scripts/test-bond-slash-flow.ts",
|
|
167
|
+
"test:bond-deferral-closure": "tsx scripts/test-bond-deferral-closure.ts",
|
|
168
|
+
"test:agent-strike-ui": "tsx scripts/test-agent-strike-ui.ts",
|
|
169
|
+
"test:notif-order-templates": "tsx scripts/test-notif-order-templates.ts",
|
|
170
|
+
"test:legacy-arbitrator-role-sweep": "tsx scripts/test-legacy-arbitrator-role-sweep.ts",
|
|
171
|
+
"test:arbitration-entry-frontend": "tsx scripts/test-arbitration-entry-frontend.ts",
|
|
172
|
+
"test:arbitrator-admin-ui": "tsx scripts/test-arbitrator-admin-ui.ts",
|
|
173
|
+
"test:webauthn-arbitrator-purposes": "tsx scripts/test-webauthn-arbitrator-purposes.ts",
|
|
174
|
+
"test:mutual-cancel": "tsx scripts/test-mutual-cancel.ts",
|
|
175
|
+
"test:mutual-cancel-route": "tsx scripts/test-mutual-cancel-route.ts",
|
|
176
|
+
"test:contribution-hub": "tsx scripts/test-contribution-hub.ts",
|
|
177
|
+
"test:evidence-active-recheck": "tsx scripts/test-evidence-active-recheck.ts",
|
|
135
178
|
"test:i18n-labels": "tsx scripts/test-i18n-discover-dispute-labels.ts",
|
|
136
179
|
"test:listing-product-type": "tsx scripts/test-listing-product-type.ts",
|
|
137
180
|
"test:external-links": "tsx scripts/test-product-external-links.ts",
|
|
@@ -196,6 +239,20 @@
|
|
|
196
239
|
"test:pv-kill-switch": "tsx scripts/test-pv-kill-switch.ts",
|
|
197
240
|
"test:pv-settlement-engine": "tsx scripts/test-pv-settlement-engine.ts",
|
|
198
241
|
"test:settlement-breakdown": "tsx scripts/test-settlement-breakdown.ts",
|
|
242
|
+
"test:dispute-noncustodial": "tsx scripts/test-dispute-noncustodial.ts",
|
|
243
|
+
"test:dispute-settlement-conservation": "tsx tests/test-dispute-settlement-conservation.ts",
|
|
244
|
+
"test:payment-query-transitions": "tsx scripts/test-payment-query-transitions.ts",
|
|
245
|
+
"test:payment-query-actions": "tsx scripts/test-payment-query-actions.ts",
|
|
246
|
+
"test:dispute-withdraw-confirm": "tsx scripts/test-dispute-withdraw-confirm.ts",
|
|
247
|
+
"test:poll-governor": "tsx scripts/test-poll-governor.ts",
|
|
248
|
+
"test:role-ui-consistency": "tsx scripts/test-role-ui-consistency.ts",
|
|
249
|
+
"test:role-switch-guard": "tsx scripts/test-role-switch-guard.ts",
|
|
250
|
+
"test:trade-terms-snapshot": "tsx scripts/test-trade-terms-snapshot.ts",
|
|
251
|
+
"test:sale-regions": "tsx scripts/test-sale-regions.ts",
|
|
252
|
+
"test:trade-tax": "tsx scripts/test-trade-tax.ts",
|
|
253
|
+
"test:listing-commerce": "tsx scripts/test-listing-commerce.ts",
|
|
254
|
+
"test:shipping-free-threshold": "tsx scripts/test-shipping-free-threshold.ts",
|
|
255
|
+
"test:purchase-terms": "tsx scripts/test-purchase-terms.ts",
|
|
199
256
|
"lint:secrets": "gitleaks detect --no-banner --redact --config .gitleaks.toml --exit-code 1",
|
|
200
257
|
"test:wallet-signer": "tsx scripts/test-wallet-signer.ts",
|
|
201
258
|
"test:cf-origin-guard": "tsx scripts/test-cf-origin-guard.ts"
|