@seasonkoh/webaz 0.1.29 → 0.1.31

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.
Files changed (126) hide show
  1. package/dist/bond-refund-blockers.js +50 -0
  2. package/dist/bond-slash.js +100 -0
  3. package/dist/bond-terms.js +31 -0
  4. package/dist/direct-pay-bond-rail-clearance.js +10 -5
  5. package/dist/direct-pay-cancel-refund.js +160 -0
  6. package/dist/direct-pay-create.js +78 -9
  7. package/dist/direct-pay-disclosures.js +17 -11
  8. package/dist/direct-pay-fee-ar.js +2 -2
  9. package/dist/direct-pay-fee-prepay-request.js +80 -0
  10. package/dist/direct-pay-returns.js +104 -0
  11. package/dist/direct-pay-stock.js +9 -0
  12. package/dist/direct-receive-deferral.js +30 -1
  13. package/dist/direct-receive-deposits.js +122 -17
  14. package/dist/free-shipping.js +37 -0
  15. package/dist/fx-rates.js +7 -0
  16. package/dist/layer0-foundation/L0-1-database/schema.js +246 -0
  17. package/dist/layer0-foundation/L0-2-state-machine/engine.js +1 -0
  18. package/dist/layer0-foundation/L0-2-state-machine/transitions.js +73 -0
  19. package/dist/layer1-agent/L1-1-mcp-server/server.js +150 -33
  20. package/dist/layer2-business/L2-6-notifications/notification-engine.js +110 -41
  21. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +133 -31
  22. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +16 -4
  23. package/dist/layer3-trust/L3-1-dispute-engine/mutual-cancel.js +156 -0
  24. package/dist/platform-receive-accounts.js +94 -0
  25. package/dist/pwa/arbitration-read-admin.js +37 -0
  26. package/dist/pwa/arbitrator-lifecycle.js +100 -0
  27. package/dist/pwa/contract-fingerprint.js +15 -0
  28. package/dist/pwa/direct-pay-order-redaction.js +20 -2
  29. package/dist/pwa/human-presence.js +2 -6
  30. package/dist/pwa/public/app-account.js +9 -9
  31. package/dist/pwa/public/app-admin-disputes.js +55 -0
  32. package/dist/pwa/public/app-agent-appeal.js +90 -0
  33. package/dist/pwa/public/app-agent-approvals.js +93 -0
  34. package/dist/pwa/public/app-agent-pair.js +127 -0
  35. package/dist/pwa/public/app-arbitrator-admin.js +87 -0
  36. package/dist/pwa/public/app-arbitrator-entry.js +9 -0
  37. package/dist/pwa/public/app-bond-deferral-ui.js +9 -0
  38. package/dist/pwa/public/app-bond-refund-ui.js +66 -0
  39. package/dist/pwa/public/app-bond-slash-ui.js +74 -0
  40. package/dist/pwa/public/app-bond-terms-ui.js +23 -0
  41. package/dist/pwa/public/app-bond-ui.js +108 -0
  42. package/dist/pwa/public/app-chat-poll.js +6 -6
  43. package/dist/pwa/public/app-contribution-hub.js +23 -0
  44. package/dist/pwa/public/app-direct-pay-accounts.js +1 -1
  45. package/dist/pwa/public/app-direct-pay-buyer.js +1 -1
  46. package/dist/pwa/public/app-direct-pay-cancel-refund.js +72 -0
  47. package/dist/pwa/public/app-direct-pay-copy.js +12 -0
  48. package/dist/pwa/public/app-direct-pay-fee-history.js +39 -0
  49. package/dist/pwa/public/app-direct-pay-fee-ops.js +1 -1
  50. package/dist/pwa/public/app-direct-pay-fee-request.js +81 -0
  51. package/dist/pwa/public/app-direct-pay-fee-requests-admin.js +70 -0
  52. package/dist/pwa/public/app-direct-pay-memo.js +14 -0
  53. package/dist/pwa/public/app-direct-pay-negotiation.js +35 -0
  54. package/dist/pwa/public/app-direct-pay-pay.js +15 -0
  55. package/dist/pwa/public/app-direct-pay-paymodal.js +32 -0
  56. package/dist/pwa/public/app-direct-pay-reconcile.js +19 -0
  57. package/dist/pwa/public/app-direct-pay-returns.js +56 -0
  58. package/dist/pwa/public/app-direct-pay-reveal.js +82 -0
  59. package/dist/pwa/public/app-direct-pay-sales-report.js +70 -0
  60. package/dist/pwa/public/app-direct-pay.js +36 -37
  61. package/dist/pwa/public/app-dispute-close-ui.js +38 -0
  62. package/dist/pwa/public/app-free-shipping-ui.js +29 -0
  63. package/dist/pwa/public/app-gmv-rail-split.js +12 -0
  64. package/dist/pwa/public/app-listing-commerce-ui.js +72 -0
  65. package/dist/pwa/public/app-mutual-cancel.js +54 -0
  66. package/dist/pwa/public/app-notif-templates-orders.js +43 -0
  67. package/dist/pwa/public/app-notif-templates.js +22 -0
  68. package/dist/pwa/public/app-order-accept-ui.js +158 -0
  69. package/dist/pwa/public/app-order-errors.js +50 -0
  70. package/dist/pwa/public/app-order-labels.js +19 -0
  71. package/dist/pwa/public/app-order-rail-filter.js +26 -0
  72. package/dist/pwa/public/app-platform-receive-accounts.js +140 -0
  73. package/dist/pwa/public/app-poll-governor.js +22 -0
  74. package/dist/pwa/public/app-profile.js +4 -4
  75. package/dist/pwa/public/app-purchase-terms-ui.js +68 -0
  76. package/dist/pwa/public/app-sale-regions-ui.js +38 -0
  77. package/dist/pwa/public/app-seller.js +1 -1
  78. package/dist/pwa/public/app-trade-tax-ui.js +33 -0
  79. package/dist/pwa/public/app.js +133 -160
  80. package/dist/pwa/public/i18n.js +675 -5
  81. package/dist/pwa/public/index.html +41 -0
  82. package/dist/pwa/public/openapi.json +719 -11
  83. package/dist/pwa/public/style.css +3 -0
  84. package/dist/pwa/routes/admin-direct-receive-deposits.js +215 -3
  85. package/dist/pwa/routes/admin-protocol-params.js +16 -0
  86. package/dist/pwa/routes/admin-reports.js +31 -5
  87. package/dist/pwa/routes/agent-governance.js +1 -1
  88. package/dist/pwa/routes/agent-grants.js +281 -32
  89. package/dist/pwa/routes/analytics.js +2 -0
  90. package/dist/pwa/routes/arbitrator.js +67 -14
  91. package/dist/pwa/routes/bond-seller.js +162 -0
  92. package/dist/pwa/routes/direct-pay-cancel-refund.js +111 -0
  93. package/dist/pwa/routes/direct-pay-disclosure-acks.js +9 -4
  94. package/dist/pwa/routes/direct-pay-pending-accept.js +222 -0
  95. package/dist/pwa/routes/direct-pay-returns.js +74 -0
  96. package/dist/pwa/routes/direct-pay-timeouts.js +186 -9
  97. package/dist/pwa/routes/disputes-read.js +20 -6
  98. package/dist/pwa/routes/disputes-write.js +91 -33
  99. package/dist/pwa/routes/external-anchors.js +4 -2
  100. package/dist/pwa/routes/fee-prepay-requests.js +66 -0
  101. package/dist/pwa/routes/governance-onboarding.js +46 -8
  102. package/dist/pwa/routes/logistics.js +4 -4
  103. package/dist/pwa/routes/me-data.js +2 -2
  104. package/dist/pwa/routes/mutual-cancel.js +62 -0
  105. package/dist/pwa/routes/orders-action.js +182 -9
  106. package/dist/pwa/routes/orders-create.js +18 -7
  107. package/dist/pwa/routes/orders-read.js +76 -14
  108. package/dist/pwa/routes/platform-receive-accounts.js +111 -0
  109. package/dist/pwa/routes/products-create.js +45 -16
  110. package/dist/pwa/routes/products-update.js +15 -1
  111. package/dist/pwa/routes/profile-identity.js +4 -2
  112. package/dist/pwa/routes/returns.js +39 -8
  113. package/dist/pwa/routes/seller-directpay-report.js +110 -0
  114. package/dist/pwa/routes/seller-quota.js +5 -1
  115. package/dist/pwa/routes/shipping-templates.js +219 -0
  116. package/dist/pwa/routes/snf.js +4 -1
  117. package/dist/pwa/routes/webauthn.js +3 -3
  118. package/dist/pwa/server.js +63 -40
  119. package/dist/runtime/agent-grant-scopes.js +69 -1
  120. package/dist/runtime/webaz-schema-helpers.js +57 -3
  121. package/dist/sale-regions.js +116 -0
  122. package/dist/shipping-templates.js +119 -0
  123. package/dist/trade-tax.js +99 -0
  124. package/dist/trade-terms.js +76 -0
  125. package/dist/version.js +1 -1
  126. package/package.json +61 -2
@@ -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 = 8;
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.29",
3
+ "version": "0.1.31",
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,13 @@
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",
91
+ "test:mcp-place-order-rail": "tsx scripts/test-mcp-place-order-rail.ts",
92
+ "test:agent-product-draft": "tsx scripts/test-agent-product-draft.ts",
85
93
  "test:agent-grant-verify": "tsx scripts/test-agent-grant-verify.ts",
86
94
  "test:agent-grant-mcp-consume": "tsx scripts/test-agent-grant-mcp-consume.ts",
87
95
  "test:connected-agents-read": "tsx scripts/test-connected-agents-read.ts",
@@ -99,7 +107,7 @@
99
107
  "test:currency-display": "tsx scripts/test-currency-display.ts",
100
108
  "test:currency-schema-flip": "tsx scripts/test-currency-schema-flip.ts",
101
109
  "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",
110
+ "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
111
  "test:create-kinds": "tsx scripts/test-create-kinds.ts",
104
112
  "test:secondhand-photo-authenticity": "tsx scripts/test-secondhand-photo-authenticity.ts",
105
113
  "test:secondhand-detail-alignment": "tsx scripts/test-secondhand-detail-alignment.ts",
@@ -123,6 +131,9 @@
123
131
  "test:direct-receive-deposits": "tsx scripts/test-direct-receive-deposits.ts",
124
132
  "test:direct-receive-accounts": "tsx scripts/test-direct-receive-accounts.ts",
125
133
  "test:direct-receive-account-endpoints": "tsx scripts/test-direct-receive-account-endpoints.ts",
134
+ "test:platform-receive-accounts": "tsx scripts/test-platform-receive-accounts.ts",
135
+ "test:fee-prepay-request": "tsx scripts/test-fee-prepay-request.ts",
136
+ "test:fee-prepay-review": "tsx scripts/test-fee-prepay-review.ts",
126
137
  "test:direct-pay-order-qr": "tsx scripts/test-direct-pay-order-qr.ts",
127
138
  "test:direct-pay-order-redaction": "tsx scripts/test-direct-pay-order-redaction.ts",
128
139
  "guard:direct-pay-order-reader": "tsx scripts/direct-pay-order-reader-guard.ts",
@@ -132,6 +143,40 @@
132
143
  "test:direct-pay-base-bond-entry": "tsx scripts/test-direct-pay-base-bond-entry.ts",
133
144
  "test:direct-pay-order-snapshot-schema": "tsx scripts/test-direct-pay-order-snapshot-schema.ts",
134
145
  "test:direct-pay-ui": "tsx scripts/test-direct-pay-ui.ts",
146
+ "test:order-errors": "tsx scripts/test-order-errors.ts",
147
+ "test:fee-request-history": "tsx scripts/test-fee-request-history.ts",
148
+ "test:i18n-parity": "tsx scripts/test-i18n-parity.ts",
149
+ "test:arbitrator-lifecycle": "tsx scripts/test-arbitrator-lifecycle.ts",
150
+ "test:arbitrator-admin-routes": "tsx scripts/test-arbitrator-admin-routes.ts",
151
+ "test:arbitrate-human-presence": "tsx scripts/test-arbitrate-human-presence.ts",
152
+ "test:arbitrate-route-e2e": "tsx scripts/test-arbitrate-route-e2e.ts",
153
+ "test:dispute-dismiss-negotiation": "tsx scripts/test-dispute-dismiss-negotiation.ts",
154
+ "test:disputes-read-auth": "tsx scripts/test-disputes-read-auth.ts",
155
+ "test:admin-dispute-triage": "tsx scripts/test-admin-dispute-triage.ts",
156
+ "test:orders-read-arbitrator-view": "tsx scripts/test-orders-read-arbitrator-view.ts",
157
+ "test:orders-rail-filter": "tsx scripts/test-orders-rail-filter.ts",
158
+ "test:directpay-sales-report": "tsx scripts/test-directpay-sales-report.ts",
159
+ "test:dashboard-gmv-by-rail": "tsx scripts/test-dashboard-gmv-by-rail.ts",
160
+ "test:direct-pay-cancel-refund": "tsx scripts/test-direct-pay-cancel-refund.ts",
161
+ "test:direct-pay-returns": "tsx scripts/test-direct-pay-returns.ts",
162
+ "test:direct-pay-pending-accept": "tsx scripts/test-direct-pay-pending-accept.ts",
163
+ "test:shipping-templates": "tsx scripts/test-shipping-templates.ts",
164
+ "test:shipping-quote": "tsx scripts/test-shipping-quote.ts",
165
+ "test:order-accept-ui": "tsx scripts/test-order-accept-ui.ts",
166
+ "test:bond-deposit-flow": "tsx scripts/test-bond-deposit-flow.ts",
167
+ "test:bond-refund-flow": "tsx scripts/test-bond-refund-flow.ts",
168
+ "test:bond-slash-flow": "tsx scripts/test-bond-slash-flow.ts",
169
+ "test:bond-deferral-closure": "tsx scripts/test-bond-deferral-closure.ts",
170
+ "test:agent-strike-ui": "tsx scripts/test-agent-strike-ui.ts",
171
+ "test:notif-order-templates": "tsx scripts/test-notif-order-templates.ts",
172
+ "test:legacy-arbitrator-role-sweep": "tsx scripts/test-legacy-arbitrator-role-sweep.ts",
173
+ "test:arbitration-entry-frontend": "tsx scripts/test-arbitration-entry-frontend.ts",
174
+ "test:arbitrator-admin-ui": "tsx scripts/test-arbitrator-admin-ui.ts",
175
+ "test:webauthn-arbitrator-purposes": "tsx scripts/test-webauthn-arbitrator-purposes.ts",
176
+ "test:mutual-cancel": "tsx scripts/test-mutual-cancel.ts",
177
+ "test:mutual-cancel-route": "tsx scripts/test-mutual-cancel-route.ts",
178
+ "test:contribution-hub": "tsx scripts/test-contribution-hub.ts",
179
+ "test:evidence-active-recheck": "tsx scripts/test-evidence-active-recheck.ts",
135
180
  "test:i18n-labels": "tsx scripts/test-i18n-discover-dispute-labels.ts",
136
181
  "test:listing-product-type": "tsx scripts/test-listing-product-type.ts",
137
182
  "test:external-links": "tsx scripts/test-product-external-links.ts",
@@ -196,6 +241,20 @@
196
241
  "test:pv-kill-switch": "tsx scripts/test-pv-kill-switch.ts",
197
242
  "test:pv-settlement-engine": "tsx scripts/test-pv-settlement-engine.ts",
198
243
  "test:settlement-breakdown": "tsx scripts/test-settlement-breakdown.ts",
244
+ "test:dispute-noncustodial": "tsx scripts/test-dispute-noncustodial.ts",
245
+ "test:dispute-settlement-conservation": "tsx tests/test-dispute-settlement-conservation.ts",
246
+ "test:payment-query-transitions": "tsx scripts/test-payment-query-transitions.ts",
247
+ "test:payment-query-actions": "tsx scripts/test-payment-query-actions.ts",
248
+ "test:dispute-withdraw-confirm": "tsx scripts/test-dispute-withdraw-confirm.ts",
249
+ "test:poll-governor": "tsx scripts/test-poll-governor.ts",
250
+ "test:role-ui-consistency": "tsx scripts/test-role-ui-consistency.ts",
251
+ "test:role-switch-guard": "tsx scripts/test-role-switch-guard.ts",
252
+ "test:trade-terms-snapshot": "tsx scripts/test-trade-terms-snapshot.ts",
253
+ "test:sale-regions": "tsx scripts/test-sale-regions.ts",
254
+ "test:trade-tax": "tsx scripts/test-trade-tax.ts",
255
+ "test:listing-commerce": "tsx scripts/test-listing-commerce.ts",
256
+ "test:shipping-free-threshold": "tsx scripts/test-shipping-free-threshold.ts",
257
+ "test:purchase-terms": "tsx scripts/test-purchase-terms.ts",
199
258
  "lint:secrets": "gitleaks detect --no-banner --redact --config .gitleaks.toml --exit-code 1",
200
259
  "test:wallet-signer": "tsx scripts/test-wallet-signer.ts",
201
260
  "test:cf-origin-guard": "tsx scripts/test-cf-origin-guard.ts"