@tbox.cn/app-contracts-mall 0.2.0 → 0.10.0

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 (84) hide show
  1. package/README.md +16 -2
  2. package/dist/alipay-activity/cards.d.ts +305 -0
  3. package/dist/assembly.d.ts +73 -7
  4. package/dist/chunk-KIZD6FZ2.js +43 -0
  5. package/dist/deployment.d.ts +32 -33
  6. package/dist/domain/activity.d.ts +64 -0
  7. package/dist/domain/coupon.d.ts +2 -0
  8. package/dist/domain/entitlement.d.ts +53 -0
  9. package/dist/domain/offer.d.ts +111 -0
  10. package/dist/domain/operation.d.ts +22 -0
  11. package/dist/domain/parking.d.ts +195 -12
  12. package/dist/domain/points.d.ts +3 -2
  13. package/dist/domain/shopping-guide.d.ts +201 -0
  14. package/dist/index.d.ts +15 -2
  15. package/dist/mall-scope-url.d.ts +13 -0
  16. package/dist/member/cards.d.ts +636 -213
  17. package/dist/parking/cards.d.ts +1271 -0
  18. package/dist/promotion/cards.d.ts +2529 -0
  19. package/dist/query-context.d.ts +47 -0
  20. package/dist/runtime.d.ts +12 -2
  21. package/dist/runtime.js +940 -86
  22. package/dist/scope.d.ts +42 -15
  23. package/dist/server/index.d.ts +11 -0
  24. package/dist/server/index.js +92 -0
  25. package/dist/server/mall-context.d.ts +24 -0
  26. package/dist/server/mall-control-plane.d.ts +10 -0
  27. package/dist/server/request-credentials.d.ts +22 -0
  28. package/dist/services/customer-service.d.ts +11 -0
  29. package/dist/services/mall-info.d.ts +39 -0
  30. package/dist/services/member.d.ts +188 -31
  31. package/dist/services/parking.d.ts +115 -11
  32. package/dist/services/promotion.d.ts +100 -4
  33. package/dist/services/shopping-guide.d.ts +97 -0
  34. package/dist/shared/cards.d.ts +42 -0
  35. package/dist/shopping-guide/cards.d.ts +3399 -561
  36. package/package.json +19 -7
  37. package/src/alipay-activity/cards.ts +66 -0
  38. package/src/assembly.ts +159 -0
  39. package/src/deployment.ts +44 -0
  40. package/src/domain/activity.ts +66 -0
  41. package/src/domain/coupon.ts +11 -0
  42. package/src/domain/discount.ts +6 -0
  43. package/src/domain/entitlement.ts +56 -0
  44. package/src/domain/offer.ts +120 -0
  45. package/src/domain/operation.ts +40 -0
  46. package/src/domain/parking.ts +207 -0
  47. package/src/domain/points.ts +15 -0
  48. package/src/domain/shopping-guide.ts +242 -0
  49. package/src/events/index.ts +10 -0
  50. package/src/guard.ts +48 -0
  51. package/src/index.ts +34 -0
  52. package/src/mall-scope-url.ts +21 -0
  53. package/src/member/cards.ts +209 -0
  54. package/src/parking/cards.ts +233 -0
  55. package/src/promotion/cards.ts +290 -0
  56. package/src/query-context.ts +81 -0
  57. package/src/runtime.ts +43 -0
  58. package/src/scope.ts +92 -0
  59. package/src/server/index.ts +11 -0
  60. package/src/server/mall-context.ts +39 -0
  61. package/src/server/mall-control-plane.ts +92 -0
  62. package/src/server/request-credentials.ts +43 -0
  63. package/src/service-slots.json +26 -0
  64. package/src/services/customer-service.ts +12 -0
  65. package/src/services/mall-info.ts +48 -0
  66. package/src/services/member.ts +386 -0
  67. package/src/services/parking.ts +182 -0
  68. package/src/services/promotion.ts +126 -0
  69. package/src/services/shopping-guide.ts +138 -0
  70. package/src/shared/cards.ts +25 -0
  71. package/src/shopping-guide/cards.ts +438 -0
  72. package/tests/assembly-validate-scope.test.ts +132 -0
  73. package/tests/cards.test.ts +586 -0
  74. package/tests/context-unification.test.ts +73 -0
  75. package/tests/mall-control-plane.test.ts +169 -0
  76. package/tests/mall-scope-url.test.ts +59 -0
  77. package/tests/member-status.test.ts +197 -0
  78. package/tests/query-context.test.ts +111 -0
  79. package/tests/request-credentials.test.ts +38 -0
  80. package/tests/resolve-primitives.test.ts +55 -0
  81. package/tests/service-slots.test.ts +83 -0
  82. package/tsconfig.build.json +15 -0
  83. package/tsconfig.json +5 -0
  84. package/dist/shopping-guide/ports.d.ts +0 -216
package/dist/runtime.js CHANGED
@@ -1,64 +1,207 @@
1
- // src/scope.ts
2
- import { z } from "zod";
3
- var MALL_SCOPE_KEY = "mall";
4
- var mallScopeSchema = z.object({
5
- miniAppId: z.string().min(1),
6
- mallId: z.string().min(1),
7
- subjectId: z.string().optional()
8
- });
9
- function parseMallScope(attributes) {
10
- const raw = attributes?.[MALL_SCOPE_KEY];
11
- if (!raw || typeof raw !== "object") return void 0;
12
- const result = mallScopeSchema.safeParse(raw);
13
- return result.success ? result.data : void 0;
1
+ import {
2
+ MALL_SCOPE_KEY,
3
+ extractMallScope,
4
+ mallScopeOf,
5
+ mallScopeSchema,
6
+ mallSlot,
7
+ parseMallScope,
8
+ readLoginMallId
9
+ } from "./chunk-KIZD6FZ2.js";
10
+
11
+ // src/query-context.ts
12
+ function mallScopeOrEmpty(reqCtx) {
13
+ return extractMallScope(reqCtx) ?? { miniAppId: "", mallId: "" };
14
14
  }
15
- function extractMallScope(ctx) {
16
- return parseMallScope(ctx.attributes);
15
+ function mallSubjectIdOf(reqCtx) {
16
+ return reqCtx.identity && reqCtx.identity.source !== "anonymous" ? reqCtx.identity.userId : void 0;
17
17
  }
18
- function requireMallScope(ctx) {
19
- return ctx.attributes[MALL_SCOPE_KEY];
18
+ function withMallScopeFromQuery(reqCtx, query, opts) {
19
+ const text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
20
+ const mallId = text(query["mallId"]);
21
+ if (!mallId) return reqCtx;
22
+ const miniAppId = text(query["miniAppId"]) ?? text(opts?.miniAppId);
23
+ return {
24
+ ...reqCtx,
25
+ attributes: {
26
+ ...reqCtx.attributes,
27
+ [MALL_SCOPE_KEY]: miniAppId ? { mallId, miniAppId } : { mallId }
28
+ }
29
+ };
30
+ }
31
+ function toMallQueryContext(reqCtx) {
32
+ const subjectId = mallSubjectIdOf(reqCtx);
33
+ return {
34
+ scope: mallScopeOrEmpty(reqCtx),
35
+ ...subjectId ? { subjectId } : {},
36
+ requestContext: reqCtx
37
+ };
20
38
  }
21
39
 
22
40
  // src/assembly.ts
41
+ var AUTH_LOGIN_SERVICE = "auth.alipay-login";
42
+ function isValidMallShape(attributes) {
43
+ const mall = attributes[MALL_SCOPE_KEY];
44
+ if (!mall || typeof mall !== "object") return true;
45
+ const mallId = mall.mallId;
46
+ return typeof mallId === "string" && mallId.length > 0;
47
+ }
23
48
  function createMallRuntimeAssembly(controlPlane) {
24
- const mallIds = new Set(controlPlane.malls.map((m) => m.mallId));
25
49
  return {
26
50
  extractScopeKey(ctx) {
27
51
  const scope = extractMallScope(ctx);
28
- if (!scope) throw new Error("Mall scope is required");
29
- return scope.mallId;
52
+ return scope?.mallId ?? "";
30
53
  },
31
54
  validateScope(attributes) {
32
- const scope = parseMallScope(attributes);
33
- return scope !== void 0 && mallIds.has(scope.mallId);
55
+ const ok = isValidMallShape(attributes);
56
+ if (!ok) {
57
+ console.error(
58
+ "[MALL] HELLO context \u5F62\u72B6\u975E\u6CD5\uFF08\u9700 mall.mallId \u975E\u7A7A\u2014\u2014URL \u53C2\u6570 ?mallId=\uFF1BminiAppId \u53EF\u9009\uFF09"
59
+ );
60
+ }
61
+ return ok;
34
62
  },
35
63
  buildScope(attributes) {
36
64
  const scope = parseMallScope(attributes);
37
65
  if (!scope) return {};
38
- return { miniAppId: scope.miniAppId, mallId: scope.mallId };
66
+ return {
67
+ mallId: scope.mallId,
68
+ ...scope.miniAppId !== void 0 ? { miniAppId: scope.miniAppId } : {}
69
+ };
39
70
  },
40
71
  extractMallScope,
41
- requireMallScope,
42
72
  controlPlane
43
73
  };
44
74
  }
75
+ function createMallContextValidator() {
76
+ return isValidMallShape;
77
+ }
78
+ function withMallName(assembly, controlPlane, fallbackName) {
79
+ return (attributes) => {
80
+ const scope = assembly.buildScope(attributes);
81
+ const mallId = parseMallScope(attributes)?.mallId;
82
+ const mall = mallId ? controlPlane.malls.find((m) => m.mallId === mallId) : void 0;
83
+ if (mall) return { ...scope, mallName: mall.name };
84
+ const fallback = mallId ? fallbackName?.(mallId) : void 0;
85
+ return fallback ? { ...scope, mallName: fallback } : scope;
86
+ };
87
+ }
88
+
89
+ // src/mall-scope-url.ts
90
+ function readMallIdFromUrl() {
91
+ const params = new URLSearchParams(window.location.search);
92
+ const mallId = params.get("mallId");
93
+ if (mallId) return mallId;
94
+ return params.get("plazaId");
95
+ }
96
+ function readMiniAppIdFromUrl() {
97
+ return new URLSearchParams(window.location.search).get("miniAppId");
98
+ }
99
+
100
+ // src/services/member.ts
101
+ var MEMBER_ACCOUNT_SERVICE = "member.account";
102
+ var MEMBER_PROFILE_SERVICE = "member.profile";
103
+ var MEMBER_CARD_SERVICE = "member.card";
104
+ var MEMBER_BENEFITS_SERVICE = "member.benefits";
105
+ var MEMBER_ENROLLMENT_SERVICE = "member.enrollment";
106
+ function classifyMemberOutcome(identity, account) {
107
+ if (identity?.source === "anonymous") return "unauthenticated";
108
+ if (!account) return "unknown";
109
+ if (account.authState === "unauthenticated") return "unauthenticated";
110
+ return account.state;
111
+ }
112
+ function externalMemberRefOf(account) {
113
+ return account.primaryMallCardNo ?? account.memberRef;
114
+ }
115
+ var MEMBER_GATE_NOTICES = {
116
+ unauthenticated: "\u8BF7\u5148\u767B\u5F55\u540E\u518D\u4F7F\u7528\u8BE5\u670D\u52A1",
117
+ not_enrolled: "\u5B8C\u6210\u5165\u4F1A\u540E\u5373\u53EF\u4F7F\u7528\u8BE5\u670D\u52A1",
118
+ closed: "\u91CD\u65B0\u5165\u4F1A\u540E\u53EF\u6062\u590D\u4F7F\u7528\u8BE5\u670D\u52A1"
119
+ };
120
+ function memberGateCardOf(userId, status) {
121
+ const { outcome } = status;
122
+ if (outcome === "unauthenticated") {
123
+ const loginUrl = status.loginUrl?.trim() || void 0;
124
+ return {
125
+ cardType: "member-login-cta",
126
+ data: { title: "\u767B\u5F55\u540E\u7EE7\u7EED", ...loginUrl ? { loginUrl } : {} },
127
+ // note 契约(W7,2026-09-01):主体+动作(模型据此答话转述——「点击卡片登录」)
128
+ note: "\u672A\u767B\u5F55 \xB7 \u70B9\u51FB\u5361\u7247\u767B\u5F55"
129
+ };
130
+ }
131
+ if (outcome !== "not_enrolled" && outcome !== "closed") return void 0;
132
+ if (!userId) return void 0;
133
+ const enrollUrl = status.enrollUrl?.trim() || void 0;
134
+ return {
135
+ cardType: "member-enrollment-cta",
136
+ data: {
137
+ userId,
138
+ authState: "authenticated",
139
+ state: outcome,
140
+ title: "\u5165\u4F1A\u540E\u7EE7\u7EED",
141
+ ...enrollUrl ? { enrollUrl } : {}
142
+ },
143
+ note: outcome === "not_enrolled" ? "\u672A\u5165\u4F1A \xB7 \u70B9\u51FB\u5361\u7247\u5B8C\u6210\u5165\u4F1A" : "\u5DF2\u9000\u4F1A \xB7 \u70B9\u51FB\u5361\u7247\u91CD\u65B0\u5165\u4F1A"
144
+ };
145
+ }
146
+ async function resolveMemberGate(factory, reqCtx) {
147
+ if (!factory) return void 0;
148
+ let status;
149
+ try {
150
+ status = await factory(reqCtx);
151
+ } catch {
152
+ return void 0;
153
+ }
154
+ const { outcome } = status;
155
+ if (outcome !== "unauthenticated" && outcome !== "not_enrolled" && outcome !== "closed") {
156
+ return void 0;
157
+ }
158
+ const card = memberGateCardOf(reqCtx.identity?.userId ?? "", status);
159
+ return {
160
+ notice: MEMBER_GATE_NOTICES[outcome],
161
+ ...card ? { card } : {}
162
+ };
163
+ }
164
+ var MEMBER_SNAPSHOT_SERVICE = "member.snapshot";
165
+
166
+ // src/services/parking.ts
167
+ var PARKING_QUERY_SERVICE = "parking.query";
168
+ var PARKING_PAYMENT_SERVICE = "parking.payment";
169
+ var PARKING_VEHICLE_SERVICE = "parking.vehicle";
170
+ var PARKING_SNAPSHOT_SERVICE = "parking.snapshot";
171
+
172
+ // src/services/promotion.ts
173
+ var PROMOTION_OFFERS_SERVICE = "promotion.offers";
174
+ var PROMOTION_ENTITLEMENTS_SERVICE = "promotion.entitlements";
175
+ var PROMOTION_ACTIVITIES_SERVICE = "promotion.activities";
176
+ var PROMOTION_ACQUISITION_SERVICE = "promotion.acquisition";
177
+ var PROMOTION_ACTIVITY_ROUTING_POLICY = "promotion.activity-routing";
178
+
179
+ // src/services/shopping-guide.ts
180
+ var SHOPPING_CATALOG_SERVICE = "shopping-guide.catalog";
181
+ var SHOPPING_SHOPS_SERVICE = "shopping-guide.shops";
182
+ var SHOPPING_CATEGORIES_SERVICE = "shopping-guide.categories";
183
+ var SHOPPING_DIRECTORY_SERVICE = "shopping-guide.directory";
184
+
185
+ // src/services/mall-info.ts
186
+ var MALL_INFO_SERVICE = "mall.info";
187
+ var MALL_INFO_CACHE_SERVICE = "mall.info-cache";
45
188
 
46
189
  // src/guard.ts
47
- import { z as z2 } from "zod";
48
- var mallLocationSchema = z2.object({
49
- latitude: z2.string().min(1),
50
- longitude: z2.string().min(1),
51
- mallId: z2.string().optional(),
52
- name: z2.string().optional()
190
+ import { z } from "zod";
191
+ var mallLocationSchema = z.object({
192
+ latitude: z.string().min(1),
193
+ longitude: z.string().min(1),
194
+ mallId: z.string().optional(),
195
+ name: z.string().optional()
53
196
  });
54
197
  function parseMallLocation(raw) {
55
198
  if (!raw || typeof raw !== "object") return void 0;
56
199
  const result = mallLocationSchema.safeParse(raw);
57
200
  return result.success ? result.data : void 0;
58
201
  }
59
- var mallUserRefSchema = z2.object({
60
- userId: z2.string().min(1),
61
- subjectId: z2.string().optional()
202
+ var mallUserRefSchema = z.object({
203
+ userId: z.string().min(1),
204
+ subjectId: z.string().optional()
62
205
  });
63
206
  function parseMallUserRef(raw) {
64
207
  if (!raw || typeof raw !== "object") return void 0;
@@ -68,6 +211,23 @@ function parseMallUserRef(raw) {
68
211
 
69
212
  // src/shopping-guide/cards.ts
70
213
  import { z as z3 } from "zod";
214
+
215
+ // src/shared/cards.ts
216
+ import { z as z2 } from "zod";
217
+ var actionDraftSchema = z2.discriminatedUnion("kind", [
218
+ z2.object({
219
+ kind: z2.literal("send"),
220
+ label: z2.string().min(1),
221
+ /** 发给服务端 AI 的完整指令(可含隐藏上下文如 merchantId);缺省用 label */
222
+ value: z2.string().optional(),
223
+ /** 用户气泡展示文本(有值时 sendMessage 走 visible=false 隐藏,只展示此文本为气泡副本);
224
+ * 缺省则 visible=true,直接展示 value/label */
225
+ visibleValue: z2.string().optional()
226
+ }),
227
+ z2.object({ kind: z2.literal("link"), label: z2.string().min(1), url: z2.string().min(1) })
228
+ ]);
229
+
230
+ // src/shopping-guide/cards.ts
71
231
  var httpsUrlSchema = z3.string().regex(/^https:\/\//).max(2048);
72
232
  var isoDateTimeSchema = z3.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/);
73
233
  var moneySchema = z3.object({
@@ -96,22 +256,34 @@ var merchantLocationSchema = z3.object({
96
256
  displayText: z3.string().min(1),
97
257
  poiId: z3.string().optional()
98
258
  });
259
+ var shopLocationSchema = merchantLocationSchema;
99
260
  var merchantStatusSchema = z3.object({
100
261
  code: z3.enum(["enabled", "disabled", "unknown"]),
101
262
  label: z3.string().min(1),
102
263
  observedAt: isoDateTimeSchema
103
264
  });
265
+ var shopStatusSchema = merchantStatusSchema;
104
266
  var pageInfoSchema = z3.object({
105
267
  page: z3.number().int().min(1),
106
- pageSize: z3.number().int().min(1).max(100),
268
+ // max 200(0.7.5 放宽,原 100):品牌导览常驻入口经 port 直调 pageSize=200 一次拉全量,
269
+ // provider 回显值入 pageInfo 需过卡 schema(card-emitter safeParse);向后兼容(旧值域 ⊂ 新值域)
270
+ pageSize: z3.number().int().min(1).max(200),
107
271
  total: z3.number().int().min(0),
108
272
  hasMore: z3.boolean()
109
273
  });
110
274
  var refinementSchema = z3.object({ label: z3.string().min(1), queryText: z3.string().min(1) });
111
- var actionDraftSchema = z3.object({
112
- actionId: z3.string().min(1),
113
- label: z3.string().min(1),
114
- input: z3.record(z3.string(), z3.unknown()).optional()
275
+ var shopCouponSchema = z3.object({
276
+ name: z3.string().min(1),
277
+ imageUrl: httpsUrlSchema.optional(),
278
+ description: z3.string().optional(),
279
+ faceValue: z3.number().optional(),
280
+ salePrice: z3.number().optional(),
281
+ applicableCategories: z3.array(z3.string()).optional(),
282
+ validHours: z3.number().optional(),
283
+ usageTimeText: z3.string().optional(),
284
+ purchaseUrl: z3.string().max(2048).optional(),
285
+ freeCouponClaimMode: z3.enum(["DIRECT", "REDIRECT"]).optional(),
286
+ offerRef: z3.string().min(1).optional()
115
287
  });
116
288
  var productListCardDataSchema = z3.object({
117
289
  context: z3.enum(["recommendation", "search", "similar", "history"]),
@@ -146,9 +318,14 @@ var productListCardDataSchema = z3.object({
146
318
  pageInfo: pageInfoSchema.optional(),
147
319
  refinements: z3.array(refinementSchema)
148
320
  });
149
- var merchantListCardDataSchema = z3.object({
321
+ var shopListCardDataSchema = z3.object({
150
322
  context: z3.enum(["recommendation", "search"]),
323
+ /** mall scope 透传:供客户端侧边栏详情 HTTP 请求携带 mallId 与 miniAppId(工具执行时注入) */
324
+ mallId: z3.string().optional(),
325
+ /** 小程序 appId(miniAppId 唯一真值透传;HTTP /shop-detail 鉴权用) */
326
+ miniAppId: z3.string().optional(),
151
327
  summary: z3.string().optional(),
328
+ categoryTabs: z3.array(z3.object({ code: z3.string(), name: z3.string() })).optional(),
152
329
  items: z3.array(
153
330
  z3.object({
154
331
  merchantId: z3.string(),
@@ -159,17 +336,21 @@ var merchantListCardDataSchema = z3.object({
159
336
  businessCategories: z3.array(z3.string()),
160
337
  location: merchantLocationSchema,
161
338
  status: merchantStatusSchema,
339
+ contactInfo: z3.array(z3.object({ channel: z3.string(), value: z3.string() })).optional(),
162
340
  services: z3.array(z3.string()),
163
341
  promotionFacts: z3.array(z3.string()),
164
342
  reasons: z3.array(reasonItemSchema).optional(),
165
343
  evidence: z3.array(evidenceSchema).optional(),
166
- itemActions: z3.array(actionDraftSchema)
344
+ itemActions: z3.array(actionDraftSchema),
345
+ /** 门店关联优惠券(列表卡透传,供点击下钻时直接展示,避免二次请求) */
346
+ coupons: z3.array(shopCouponSchema).optional()
167
347
  })
168
348
  ),
169
349
  noResultsReason: z3.string().optional(),
170
350
  pageInfo: pageInfoSchema.optional(),
171
351
  refinements: z3.array(refinementSchema)
172
352
  });
353
+ var merchantListCardDataSchema = shopListCardDataSchema;
173
354
  var productDetailCardDataSchema = z3.object({
174
355
  productId: z3.string().min(1),
175
356
  name: z3.string().min(1),
@@ -195,9 +376,13 @@ var productDetailCardDataSchema = z3.object({
195
376
  humanGuideAvailable: z3.boolean(),
196
377
  itemActions: z3.array(actionDraftSchema)
197
378
  });
198
- var merchantDetailCardDataSchema = z3.object({
379
+ var shopDetailCardDataSchema = z3.object({
199
380
  merchantId: z3.string(),
200
381
  name: z3.string(),
382
+ /** mall scope 透传:供客户端 Sheet 后台 detail HTTP 请求携带(工具执行时注入,与 shop-list 同源) */
383
+ mallId: z3.string().optional(),
384
+ /** 小程序 appId(miniAppId 唯一真值透传;HTTP /shop-detail 鉴权用) */
385
+ miniAppId: z3.string().optional(),
201
386
  logoUrl: httpsUrlSchema.optional(),
202
387
  coverUrls: z3.array(httpsUrlSchema),
203
388
  brand: z3.string().optional(),
@@ -208,19 +393,43 @@ var merchantDetailCardDataSchema = z3.object({
208
393
  contactInfo: z3.array(z3.object({ channel: z3.string(), value: z3.string() })).optional(),
209
394
  services: z3.array(z3.string()),
210
395
  promotionFacts: z3.array(z3.string()),
396
+ /** 门店关联优惠券列表(来自 couponList;name/imageUrl/faceValue/salePrice/applicableCategories/validHours) */
397
+ coupons: z3.array(shopCouponSchema).optional(),
211
398
  evidence: z3.array(evidenceSchema),
212
399
  itemActions: z3.array(actionDraftSchema)
213
400
  });
401
+ var merchantDetailCardDataSchema = shopDetailCardDataSchema;
214
402
  var categoryListCardDataSchema = z3.object({
215
403
  categories: z3.array(
216
404
  z3.object({
217
405
  code: z3.string().min(1),
218
406
  name: z3.string().min(1),
219
407
  description: z3.string().optional(),
408
+ /** 类目图标 URL;undefined 时前端展示默认图标 */
409
+ iconUrl: httpsUrlSchema.optional(),
220
410
  merchantCount: z3.number().int().min(0),
221
- subCategories: z3.array(z3.object({ code: z3.string(), name: z3.string(), merchantCount: z3.number().int().min(0) })).optional()
411
+ subCategories: z3.array(z3.object({ code: z3.string(), name: z3.string(), merchantCount: z3.number().int().min(0) })).optional(),
412
+ /** 0.3.2 D4:类别条目动作(send 合成话术/外链) */
413
+ itemActions: z3.array(actionDraftSchema).optional()
222
414
  })
223
415
  ),
416
+ /** 商场楼栋-楼层结构(与 mall-directory 同语义子集,供 category-list 卡片展示楼层导航) */
417
+ buildings: z3.array(
418
+ z3.object({
419
+ buildingCode: z3.string(),
420
+ buildingName: z3.string(),
421
+ floors: z3.array(
422
+ z3.object({
423
+ floorCode: z3.string(),
424
+ floorName: z3.string(),
425
+ businessCategories: z3.array(z3.string()),
426
+ merchantCount: z3.number().int().min(0),
427
+ /** 楼层条目动作(send:查看该楼层门店列表) */
428
+ itemActions: z3.array(actionDraftSchema).optional()
429
+ })
430
+ )
431
+ })
432
+ ).optional(),
224
433
  observedAt: isoDateTimeSchema
225
434
  });
226
435
  var productComparisonCardDataSchema = z3.object({
@@ -242,6 +451,8 @@ var productComparisonCardDataSchema = z3.object({
242
451
  caveats: z3.array(z3.string())
243
452
  })
244
453
  ),
454
+ /** 0.3.2 D4:刷新截止时间(ISO);到期后渲染端禁用全部条目交互 */
455
+ refreshRequiredAt: isoDateTimeSchema.optional(),
245
456
  observedAt: isoDateTimeSchema
246
457
  });
247
458
  var mallDirectoryCardDataSchema = z3.object({
@@ -256,7 +467,9 @@ var mallDirectoryCardDataSchema = z3.object({
256
467
  floorCode: z3.string(),
257
468
  floorName: z3.string(),
258
469
  businessCategories: z3.array(z3.string()),
259
- merchantCount: z3.number().int().min(0)
470
+ merchantCount: z3.number().int().min(0),
471
+ /** 0.3.2 D4:楼层条目动作(send:查看楼层门店) */
472
+ itemActions: z3.array(actionDraftSchema).optional()
260
473
  })
261
474
  )
262
475
  })
@@ -283,9 +496,13 @@ var customerServiceFaqCardDataSchema = z3.object({
283
496
  question: z3.string().min(1),
284
497
  answer: z3.string().min(1),
285
498
  category: z3.string().optional(),
286
- relevanceScore: z3.number().min(0).max(1)
499
+ relevanceScore: z3.number().min(0).max(1),
500
+ /** 0.3.2 D4:答案参考外链(openLink) */
501
+ referenceUrl: httpsUrlSchema.optional()
287
502
  })
288
503
  ),
504
+ /** 客服电话(015:deployment.malls 经 mall-directory 服务注入;缺省不渲染拨打条目) */
505
+ contactPhone: z3.string().min(1).optional(),
289
506
  noResultsReason: z3.string().optional()
290
507
  });
291
508
  var humanGuideListCardDataSchema = z3.object({
@@ -314,25 +531,75 @@ var locationHandoffCardDataSchema = z3.object({
314
531
  }).optional(),
315
532
  itemActions: z3.array(actionDraftSchema)
316
533
  });
534
+ var recommendMerchantsCardDataSchema = z3.object({
535
+ summary: z3.string().optional(),
536
+ /** 类目筛选 Tab(来自 list-categories;有值时渲染 Tab 栏) */
537
+ categoryTabs: z3.array(
538
+ z3.object({
539
+ code: z3.string(),
540
+ name: z3.string()
541
+ })
542
+ ).optional(),
543
+ items: z3.array(
544
+ z3.object({
545
+ merchantId: z3.string(),
546
+ name: z3.string(),
547
+ logoUrl: httpsUrlSchema.optional(),
548
+ coverUrl: httpsUrlSchema.optional(),
549
+ brand: z3.string().optional(),
550
+ businessCategories: z3.array(z3.string()),
551
+ location: merchantLocationSchema,
552
+ status: merchantStatusSchema,
553
+ description: z3.string().optional(),
554
+ contactInfo: z3.array(z3.object({ channel: z3.string(), value: z3.string() })).optional(),
555
+ services: z3.array(z3.string()).optional(),
556
+ /** 营业时间/经营状态文本(如 "10:00营业"),来自知识库 经营状态 字段 */
557
+ operatingStatus: z3.string().optional(),
558
+ /** 评分(如 "4.8"),来自知识库 评分 字段 */
559
+ rating: z3.string().optional(),
560
+ /** 人均消费文本(如 "¥202"),来自知识库 人均消费 字段(分为单位时自动格式化) */
561
+ avgSpend: z3.string().optional(),
562
+ promotionFacts: z3.array(z3.string()),
563
+ itemActions: z3.array(actionDraftSchema)
564
+ })
565
+ ),
566
+ noResultsReason: z3.string().optional(),
567
+ observedAt: isoDateTimeSchema
568
+ });
317
569
  var shoppingGuideCardDataSchemas = {
318
570
  "product-list": productListCardDataSchema,
319
571
  "product-detail": productDetailCardDataSchema,
320
- "merchant-list": merchantListCardDataSchema,
321
- "merchant-detail": merchantDetailCardDataSchema,
572
+ "shop-list": shopListCardDataSchema,
573
+ "shop-detail": shopDetailCardDataSchema,
322
574
  "category-list": categoryListCardDataSchema,
323
575
  "product-comparison": productComparisonCardDataSchema,
324
576
  "mall-directory": mallDirectoryCardDataSchema,
325
577
  "nearby-mall-list": nearbyMallListCardDataSchema,
326
578
  "customer-service-faq": customerServiceFaqCardDataSchema,
327
579
  "human-guide-list": humanGuideListCardDataSchema,
328
- "location-handoff": locationHandoffCardDataSchema
580
+ "location-handoff": locationHandoffCardDataSchema,
581
+ "recommend-merchants": recommendMerchantsCardDataSchema
329
582
  };
330
583
 
331
584
  // src/member/cards.ts
332
585
  import { z as z4 } from "zod";
333
- var memberStateSchema = z4.enum(["active", "not_enrolled", "closed"]);
586
+ var memberAuthStateSchema = z4.enum(["authenticated", "unauthenticated"]);
587
+ var memberStateSchema = z4.enum(["unknown", "active", "not_enrolled", "closed"]);
588
+ var MEMBER_PREFERENCE_VALUES = [
589
+ "\u6211\u5E38\u6765\u4E70\u8863\u670D",
590
+ "\u6211\u5E38\u5E26\u5A03\u6765\u73A9",
591
+ "\u6211\u5E38\u6765\u5403\u7F8E\u98DF"
592
+ ];
593
+ var memberPreferenceValueSchema = z4.enum(MEMBER_PREFERENCE_VALUES);
594
+ var MEMBER_PREFERENCE_PROMPTS = MEMBER_PREFERENCE_VALUES.map((value) => ({ value, query: value }));
595
+ var MEMBER_PREFERENCE_RECOMMENDATION_QUERIES = {
596
+ "\u6211\u5E38\u6765\u4E70\u8863\u670D": "\u63A8\u8350\u9002\u5408\u8D2D\u4E70\u7684\u670D\u88C5\u95E8\u5E97\u548C\u5546\u54C1",
597
+ "\u6211\u5E38\u5E26\u5A03\u6765\u73A9": "\u63A8\u8350\u9002\u5408\u5E26\u5A03\u7684\u4EB2\u5B50\u5A31\u4E50\u95E8\u5E97",
598
+ "\u6211\u5E38\u6765\u5403\u7F8E\u98DF": "\u63A8\u8350\u503C\u5F97\u53BB\u7684\u9910\u996E\u7F8E\u98DF\u95E8\u5E97"
599
+ };
334
600
  var memberInfoCardDataSchema = z4.object({
335
601
  userId: z4.string().min(1),
602
+ authState: memberAuthStateSchema,
336
603
  state: memberStateSchema,
337
604
  displayName: z4.string().optional(),
338
605
  avatarUrl: z4.string().optional(),
@@ -348,35 +615,42 @@ var memberCardSummaryCardDataSchema = z4.object({
348
615
  status: z4.enum(["active", "inactive", "frozen", "expired", "unknown"]),
349
616
  levelName: z4.string().optional(),
350
617
  availablePoints: z4.number().int().min(0),
351
- cardImageUrl: z4.string().optional()
352
- });
353
- var memberCouponListCardDataSchema = z4.object({
354
- userId: z4.string().min(1),
355
- items: z4.array(
356
- z4.object({
357
- couponRef: z4.string(),
358
- title: z4.string(),
359
- type: z4.enum(["amount", "discount", "gift", "parking", "unknown"]),
360
- status: z4.enum(["effective", "used", "expired", "invalid", "unknown"]),
361
- benefitText: z4.string().optional(),
362
- thresholdText: z4.string().optional()
363
- })
364
- ),
365
- total: z4.number().int().min(0)
366
- });
367
- var memberCouponDetailCardDataSchema = z4.object({
368
- couponRef: z4.string(),
369
- title: z4.string(),
370
- type: z4.enum(["amount", "discount", "gift", "parking", "unknown"]),
371
- status: z4.enum(["effective", "used", "expired", "invalid", "unknown"]),
372
- description: z4.string().optional(),
373
- benefitText: z4.string().optional(),
374
- thresholdText: z4.string().optional()
618
+ cardImageUrl: z4.string().optional(),
619
+ pointsRuleImageUrl: z4.string().url().optional(),
620
+ pointsRuleContent: z4.string().min(1).max(2e4).optional(),
621
+ pointsUrl: z4.string().url().optional(),
622
+ presentation: z4.object({
623
+ variant: z4.enum(["blue", "silver", "gold", "black"]),
624
+ labelsEmbedded: z4.boolean().optional()
625
+ }).optional(),
626
+ progress: z4.object({
627
+ daySpentCents: z4.number().int().min(0),
628
+ yearSpentCents: z4.number().int().min(0),
629
+ percent: z4.number().min(0).max(100),
630
+ message: z4.string().min(1).max(256)
631
+ }).optional(),
632
+ benefits: z4.array(z4.object({
633
+ benefitRef: z4.string().min(1),
634
+ name: z4.string().min(1).max(128),
635
+ iconUrl: z4.string().url().optional(),
636
+ description: z4.string().min(1).max(128).optional(),
637
+ action: z4.object({
638
+ kind: z4.enum(["miniapp", "dialog", "toast"]),
639
+ url: z4.string().url().optional(),
640
+ title: z4.string().min(1).max(128).optional(),
641
+ text: z4.string().min(1).max(2048).optional()
642
+ }).optional()
643
+ })).max(20).optional(),
644
+ /** 入会后偏好引导;首批固定三项,保留数组形态便于后续扩展。 */
645
+ preferencePrompts: z4.array(z4.object({
646
+ value: memberPreferenceValueSchema,
647
+ query: z4.string().min(1).max(128)
648
+ })).max(8).optional()
375
649
  });
376
650
  var memberPointUsageListCardDataSchema = z4.object({
377
651
  userId: z4.string().min(1),
378
652
  balance: z4.number().int(),
379
- level: z4.enum(["silver", "gold", "platinum"]),
653
+ membershipLabel: z4.string().optional(),
380
654
  items: z4.array(
381
655
  z4.object({
382
656
  usageRecordRef: z4.string(),
@@ -391,33 +665,579 @@ var memberPointUsageListCardDataSchema = z4.object({
391
665
  var memberEnrollmentFormCardDataSchema = z4.object({
392
666
  userId: z4.string().min(1),
393
667
  state: memberStateSchema,
394
- /** 表单必填项(渲染端收集后经 componentAction member.enroll 提交) */
395
- fields: z4.array(z4.object({ fieldId: z4.string(), label: z4.string(), required: z4.boolean() })),
396
- actionId: z4.string()
668
+ /** 表单版本(幂等键组成部分) */
669
+ formVersion: z4.string().min(1),
670
+ /** 表单字段(readOnly = 授权预填锁定;prefill = 预填值) */
671
+ fields: z4.array(
672
+ z4.object({
673
+ fieldId: z4.string(),
674
+ label: z4.string(),
675
+ type: z4.enum(["text", "tel", "select", "date"]),
676
+ required: z4.boolean(),
677
+ readOnly: z4.boolean().optional(),
678
+ prefill: z4.string().optional(),
679
+ placeholder: z4.string().optional(),
680
+ options: z4.array(z4.string()).optional()
681
+ })
682
+ ),
683
+ /** 隐私/条款同意项(required 必勾) */
684
+ consents: z4.array(
685
+ z4.object({
686
+ consentId: z4.string(),
687
+ version: z4.string(),
688
+ title: z4.string(),
689
+ required: z4.boolean(),
690
+ url: z4.string().optional()
691
+ })
692
+ ),
693
+ /** 支付宝授权状态(required → 渲染授权按钮;authorized → mobile 预填 readOnly) */
694
+ authorization: z4.object({
695
+ status: z4.enum(["required", "authorized", "unavailable"]),
696
+ mobileMasked: z4.string().optional()
697
+ }),
698
+ /** 手动填写模式提示(授权不可用降级) */
699
+ manualNotice: z4.string().optional()
397
700
  });
398
701
  var memberEnrollmentStatusCardDataSchema = z4.object({
399
702
  userId: z4.string().min(1),
400
- success: z4.boolean(),
703
+ attemptRef: z4.string().optional(),
704
+ state: z4.enum(["pending_confirmation", "confirmation_timeout", "active", "failed"]),
401
705
  memberRef: z4.string().optional(),
402
- message: z4.string().optional(),
403
- state: memberStateSchema
706
+ message: z4.string().optional()
707
+ });
708
+ var memberEnrollmentCtaCardDataSchema = z4.object({
709
+ userId: z4.string().min(1),
710
+ authState: z4.literal("authenticated"),
711
+ state: z4.enum(["not_enrolled", "closed"]),
712
+ /**
713
+ * alipays:// 深链,由部署配置注入;**可选透传素材**——出卡不依赖它,
714
+ * 壳能力(getLoginState/openGate)缺席时兜底 openLink 用,未配置恒出卡(无此字段)
715
+ */
716
+ enrollUrl: z4.string().min(1).refine((value) => value.startsWith("alipays://") || value.startsWith("https://"), {
717
+ message: "enrollUrl must use alipays:// or https://"
718
+ }).optional(),
719
+ title: z4.string().optional(),
720
+ subtitle: z4.string().optional(),
721
+ /**
722
+ * 入会权益提示列表(工具层注入通用文案;不得包含折扣/免费停车等未经系统确认的承诺)。
723
+ * 缺省不展示,由渲染层按有无决定是否渲染区块。
724
+ */
725
+ perks: z4.array(z4.string()).optional()
726
+ });
727
+ var memberLoginCtaCardDataSchema = z4.object({
728
+ /**
729
+ * 商场小程序登录页深链,由部署配置注入;**可选透传素材**——出卡不依赖它,
730
+ * 壳能力(getLoginState)缺席时兜底 openLink 用,未配置恒出卡(无此字段)
731
+ */
732
+ loginUrl: z4.string().min(1).refine((value) => value.startsWith("alipays://") || value.startsWith("https://"), {
733
+ message: "loginUrl must use alipays:// or https://"
734
+ }).optional(),
735
+ title: z4.string().optional()
404
736
  });
405
737
  var memberCardDataSchemas = {
406
738
  "member-info": memberInfoCardDataSchema,
407
739
  "member-card-summary": memberCardSummaryCardDataSchema,
408
- "member-coupon-list": memberCouponListCardDataSchema,
409
- "member-coupon-detail": memberCouponDetailCardDataSchema,
410
740
  "member-point-usage-list": memberPointUsageListCardDataSchema,
411
741
  "member-enrollment-form": memberEnrollmentFormCardDataSchema,
412
- "member-enrollment-status": memberEnrollmentStatusCardDataSchema
742
+ "member-enrollment-status": memberEnrollmentStatusCardDataSchema,
743
+ "member-enrollment-cta": memberEnrollmentCtaCardDataSchema,
744
+ "member-login-cta": memberLoginCtaCardDataSchema
745
+ };
746
+
747
+ // src/promotion/cards.ts
748
+ import { z as z6 } from "zod";
749
+
750
+ // src/alipay-activity/cards.ts
751
+ import { z as z5 } from "zod";
752
+ var bumpInVoucherSchema = z5.object({
753
+ prizeId: z5.string(),
754
+ voucherName: z5.string(),
755
+ voucherType: z5.string(),
756
+ reductionAmount: z5.string(),
757
+ thresholdAmountText: z5.string(),
758
+ unit: z5.string(),
759
+ merchantName: z5.string(),
760
+ merchantLogo: z5.string().optional(),
761
+ itemLogo: z5.string().optional()
762
+ });
763
+ var bumpInActStatusSchema = z5.enum([
764
+ "NOT_START",
765
+ "INIT",
766
+ "PROCESSING",
767
+ "FINISH",
768
+ "RECEIVED"
769
+ ]);
770
+ var promotionBumpInActivityCardDataSchema = z5.object({
771
+ activityRef: z5.string(),
772
+ title: z5.string(),
773
+ mainTitle: z5.string().optional(),
774
+ coverUrl: z5.string().optional(),
775
+ /** 联单活动全幅背景图 URL(与 coverUrl 互斥;backgroundUrl 存在时头部采用全幅覆盖布局) */
776
+ backgroundUrl: z5.string().optional(),
777
+ /** 左上角活动类型胶囊标签,如"碰一下联单" */
778
+ tagLabel: z5.string().optional(),
779
+ cardLogoUrl: z5.string().optional(),
780
+ actStatus: bumpInActStatusSchema,
781
+ totalProgress: z5.string().optional(),
782
+ currentProgress: z5.string().optional(),
783
+ progressUnit: z5.string().optional(),
784
+ startAtText: z5.string().optional(),
785
+ endAtText: z5.string().optional(),
786
+ description: z5.string().optional(),
787
+ vouchers: z5.array(bumpInVoucherSchema),
788
+ mode: z5.enum(["detail", "banner"]).default("detail"),
789
+ /** bar(线性进度条,默认)| steps(圆形步骤节点,适合联单任务) */
790
+ progressStyle: z5.enum(["bar", "steps"]).default("bar"),
791
+ /** 参与步骤(联单活动 guide.steps;banner 模式用于渲染步骤进度节点) */
792
+ steps: z5.array(z5.object({ title: z5.string(), description: z5.string().optional() })).optional()
793
+ });
794
+
795
+ // src/promotion/cards.ts
796
+ var imageUrlSchema = z6.string().max(2048).optional();
797
+ var freeCouponClaimModeSchema = z6.enum(["DIRECT", "REDIRECT"]);
798
+ var offerSummarySchema = z6.object({
799
+ offerRef: z6.string().min(1),
800
+ title: z6.string().min(1),
801
+ subtitle: z6.string().optional(),
802
+ offerType: z6.enum(["COUPON", "BUNDLE", "PROMOTION", "ACTIVITY", "GROUPON"]),
803
+ /** 主 facet 一句话(如「满 100 减 20」) */
804
+ benefitText: z6.string().optional(),
805
+ /** 使用门槛(如「满 300 元可用」) */
806
+ thresholdText: z6.string().optional(),
807
+ /**
808
+ * 券的**售价**展示,与 benefitText 是两回事——后者说优惠内容,这里说买这张券要花多少。
809
+ * 免费券给「免费」,付费券给「¥28.8」。文案由 provider 直接给:各厂商币种、小数位与
810
+ * 「免费/0 元」措辞不一,客户端不做数值运算也不反解金额。
811
+ */
812
+ priceText: z6.string().optional(),
813
+ /** 划线原价(如「¥38.8」)。与 priceText 成对出现才有意义,单独给不渲染。 */
814
+ originalPriceText: z6.string().optional(),
815
+ /** 折扣角标(如「7.5折」)。同样是 provider 直出文案,不由前端拿两个价格算。 */
816
+ discountLabel: z6.string().optional(),
817
+ /**
818
+ * 领取按钮文案:免费券「领取」、付费券「抢购」。由 provider 给而非前端按 priceText
819
+ * 猜——「免费」的写法各家不同,猜错就把付费券写成领取。缺省回落「领取」。
820
+ */
821
+ claimActionLabel: z6.string().optional(),
822
+ purchaseUrl: z6.string().max(2048).optional(),
823
+ freeCouponClaimMode: freeCouponClaimModeSchema.optional(),
824
+ /**
825
+ * 下面三条供**列表内的详情浮层**渲染——浮层就地展开,没有二次请求可拉,
826
+ * 需要的信息必须随列表一起下发。厂商不给就不渲染对应区块。
827
+ */
828
+ stockText: z6.string().optional(),
829
+ usageNoticeText: z6.string().optional(),
830
+ guaranteeText: z6.string().optional(),
831
+ usageSceneText: z6.string().optional(),
832
+ usageTimeText: z6.string().optional(),
833
+ claimTimeText: z6.string().optional(),
834
+ usageRuleText: z6.string().optional(),
835
+ /** 可领取(availability.status === 'AVAILABLE') */
836
+ claimable: z6.boolean(),
837
+ availabilityStatus: z6.enum(["AVAILABLE", "UNAVAILABLE", "REQUIRES_ACTION", "UNKNOWN"]).optional(),
838
+ validityText: z6.string().optional(),
839
+ imageUrl: imageUrlSchema,
840
+ /** 0.3.2 D4:条目动作(send:查看详情) */
841
+ itemActions: z6.array(actionDraftSchema).optional(),
842
+ tags: z6.array(z6.string())
843
+ });
844
+ var promotionOfferListCardDataSchema = z6.object({
845
+ presentation: z6.enum(["offer", "promotion", "coupon", "recommendation"]).optional(),
846
+ mode: z6.enum(["search", "claimable", "recommend"]),
847
+ /** 客户端默认展示 4 条;传入正整数可覆盖该上限。 */
848
+ maxItems: z6.number().int().positive().optional(),
849
+ summary: z6.string().optional(),
850
+ items: z6.array(offerSummarySchema),
851
+ noResultsReason: z6.string().optional(),
852
+ nextCursor: z6.string().optional()
853
+ });
854
+ var promotionOfferDetailCardDataSchema = z6.object({
855
+ presentation: z6.enum(["offer", "promotion", "promotion-guide", "coupon", "package"]).optional(),
856
+ offerRef: z6.string().min(1),
857
+ title: z6.string().min(1),
858
+ offerType: z6.enum(["COUPON", "BUNDLE", "PROMOTION", "ACTIVITY", "GROUPON"]),
859
+ subtitle: z6.string().optional(),
860
+ description: z6.string().optional(),
861
+ imageUrl: imageUrlSchema,
862
+ benefitText: z6.string().optional(),
863
+ thresholdText: z6.string().optional(),
864
+ validityText: z6.string().optional(),
865
+ /** 售价三件套,语义同 offerSummary(provider 直出文案,前端不做运算) */
866
+ priceText: z6.string().optional(),
867
+ originalPriceText: z6.string().optional(),
868
+ discountLabel: z6.string().optional(),
869
+ claimActionLabel: z6.string().optional(),
870
+ purchaseUrl: z6.string().max(2048).optional(),
871
+ /** 剩余库存文案(如「剩余:29」);厂商不给就不展示,别拿 0 当「售罄」用 */
872
+ stockText: z6.string().optional(),
873
+ usageNoticeText: z6.string().optional(),
874
+ /** 核销场景(如「万铺小二核销」)——告诉用户这张券在哪、怎么用掉 */
875
+ usageSceneText: z6.string().optional(),
876
+ usageTimeText: z6.string().optional(),
877
+ claimTimeText: z6.string().optional(),
878
+ usageRuleText: z6.string().optional(),
879
+ guaranteeText: z6.string().optional(),
880
+ /** 使用规则/参与步骤 */
881
+ rules: z6.array(z6.string()),
882
+ participationMode: z6.enum([
883
+ "AUTOMATIC_DISCOUNT",
884
+ "CLAIM_THEN_PURCHASE",
885
+ "REGISTER_THEN_ATTEND",
886
+ "GROUP_PURCHASE",
887
+ "UNKNOWN"
888
+ ]).optional(),
889
+ steps: z6.array(z6.object({ title: z6.string(), description: z6.string().optional() })).optional(),
890
+ requirements: z6.array(z6.string()).optional(),
891
+ notice: z6.string().optional(),
892
+ /** 厂商可提供时展示适用门店;可选以兼容历史卡数据。 */
893
+ stores: z6.array(z6.object({
894
+ storeId: z6.string().min(1),
895
+ storeName: z6.string().min(1),
896
+ businessName: z6.string().optional(),
897
+ address: z6.string().optional(),
898
+ floor: z6.string().optional()
899
+ })).optional(),
900
+ bundleItems: z6.array(z6.object({ name: z6.string(), quantity: z6.number().int().positive() })).optional(),
901
+ /** 领取动作是否可用(未登录会员/不可用 → false + unavailableReason) */
902
+ claimable: z6.boolean(),
903
+ unavailableReason: z6.string().optional(),
904
+ caveats: z6.array(z6.string())
905
+ });
906
+ var promotionEntitlementListCardDataSchema = z6.object({
907
+ mallId: z6.string().optional(),
908
+ miniAppId: z6.string().optional(),
909
+ /** 客户端默认展示 4 条;传入正整数可覆盖该上限。 */
910
+ maxItems: z6.number().int().positive().optional(),
911
+ items: z6.array(
912
+ z6.object({
913
+ entitlementRef: z6.string().min(1),
914
+ title: z6.string().min(1),
915
+ imageUrl: imageUrlSchema,
916
+ entitlementType: z6.enum(["COUPON", "BUNDLE", "PROMOTION", "ACTIVITY", "GROUPON"]),
917
+ status: z6.enum(["EFFECTIVE", "USED", "EXPIRED", "INVALID", "UNKNOWN"]),
918
+ codeMasked: z6.string().optional(),
919
+ code: z6.string().optional(),
920
+ qrCodeEnabled: z6.boolean().optional(),
921
+ benefitText: z6.string().optional(),
922
+ thresholdText: z6.string().optional(),
923
+ usageNotice: z6.string().optional(),
924
+ guaranteeText: z6.string().optional(),
925
+ usageSceneText: z6.string().optional(),
926
+ usageTimeText: z6.string().optional(),
927
+ claimTimeText: z6.string().optional(),
928
+ usageRuleText: z6.string().optional(),
929
+ validityText: z6.string().optional(),
930
+ validUntilText: z6.string().optional(),
931
+ /** 0.3.2 D4:条目动作(send:查看详情) */
932
+ itemActions: z6.array(actionDraftSchema).optional(),
933
+ stores: z6.array(z6.object({
934
+ storeId: z6.string(),
935
+ storeName: z6.string(),
936
+ businessName: z6.string().optional(),
937
+ imageUrl: imageUrlSchema,
938
+ address: z6.string().optional(),
939
+ floor: z6.string().optional(),
940
+ latitude: z6.number().optional(),
941
+ longitude: z6.number().optional()
942
+ })).optional()
943
+ })
944
+ ),
945
+ noResultsReason: z6.string().optional(),
946
+ nextCursor: z6.string().optional()
947
+ });
948
+ var promotionActivityCategoryListCardDataSchema = z6.object({
949
+ items: z6.array(
950
+ z6.object({
951
+ categoryId: z6.string().min(1),
952
+ name: z6.string().min(1),
953
+ parentId: z6.string().optional(),
954
+ itemActions: z6.array(actionDraftSchema).optional()
955
+ })
956
+ ),
957
+ noResultsReason: z6.string().optional()
958
+ });
959
+ var promotionBestDealCardDataSchema = z6.object({
960
+ authoritative: z6.boolean(),
961
+ originalAmountCents: z6.number().int().min(0),
962
+ savingsCents: z6.number().int().min(0).optional(),
963
+ expectedPayCents: z6.number().int().min(0).optional(),
964
+ appliedOfferRefs: z6.array(z6.string()),
965
+ candidates: z6.array(offerSummarySchema).optional(),
966
+ message: z6.string().optional()
967
+ });
968
+ var promotionActivityListCardDataSchema = z6.object({
969
+ items: z6.array(
970
+ z6.object({
971
+ activityRef: z6.string().min(1),
972
+ title: z6.string().min(1),
973
+ subtitle: z6.string().optional(),
974
+ coverUrl: imageUrlSchema,
975
+ registrationRequired: z6.boolean(),
976
+ jumpOnly: z6.boolean().optional(),
977
+ jumpLabel: z6.string().min(1).optional(),
978
+ remainingQuota: z6.number().int().min(0).optional(),
979
+ periodText: z6.string().optional(),
980
+ categoryName: z6.string().optional(),
981
+ availabilityStatus: z6.enum(["AVAILABLE", "FULL", "ENDED", "UPCOMING", "UNKNOWN"]).optional(),
982
+ itemActions: z6.array(actionDraftSchema).optional()
983
+ })
984
+ ),
985
+ noResultsReason: z6.string().optional(),
986
+ nextCursor: z6.string().optional()
987
+ });
988
+ var promotionActivityDetailCardDataSchema = z6.object({
989
+ presentation: z6.enum(["detail", "schedule", "guide"]).optional(),
990
+ activityRef: z6.string().min(1),
991
+ title: z6.string().min(1),
992
+ subtitle: z6.string().optional(),
993
+ categoryName: z6.string().optional(),
994
+ description: z6.string().optional(),
995
+ coverUrl: imageUrlSchema,
996
+ periodText: z6.string().optional(),
997
+ location: z6.string().optional(),
998
+ registrationRequired: z6.boolean(),
999
+ jumpOnly: z6.boolean().optional(),
1000
+ jumpLabel: z6.string().min(1).optional(),
1001
+ /** 需报名时直达厂商支付宝小程序活动详情;由客户端 openLink 本地处理。 */
1002
+ registrationUrl: z6.string().max(2048).optional(),
1003
+ remainingQuota: z6.number().int().min(0).optional(),
1004
+ sessions: z6.array(
1005
+ z6.object({
1006
+ sessionRef: z6.string(),
1007
+ title: z6.string(),
1008
+ startText: z6.string(),
1009
+ endText: z6.string().optional(),
1010
+ location: z6.string().optional(),
1011
+ remainingQuota: z6.number().int().min(0).optional(),
1012
+ status: z6.enum(["OPEN", "FULL", "CLOSED", "UNKNOWN"])
1013
+ })
1014
+ ),
1015
+ guideSteps: z6.array(z6.string()),
1016
+ guideItems: z6.array(z6.object({ title: z6.string(), description: z6.string().optional() })).optional(),
1017
+ conditions: z6.array(z6.string()),
1018
+ notice: z6.string().optional()
1019
+ });
1020
+ var promotionCardDataSchemas = {
1021
+ "promotion-offer-list": promotionOfferListCardDataSchema,
1022
+ "promotion-offer-detail": promotionOfferDetailCardDataSchema,
1023
+ "promotion-entitlement-list": promotionEntitlementListCardDataSchema,
1024
+ "promotion-activity-category-list": promotionActivityCategoryListCardDataSchema,
1025
+ "promotion-best-deal": promotionBestDealCardDataSchema,
1026
+ "promotion-activity-list": promotionActivityListCardDataSchema,
1027
+ "promotion-activity-detail": promotionActivityDetailCardDataSchema
1028
+ };
1029
+
1030
+ // src/parking/cards.ts
1031
+ import { z as z7 } from "zod";
1032
+ var parkingOrderInfoSchema = z7.object({
1033
+ /** 停车秒数 */
1034
+ staySecond: z7.number().int().nonnegative().optional(),
1035
+ /** 停车费,单位分 */
1036
+ parkingFee: z7.number().int().nonnegative().optional(),
1037
+ /** 车牌绑定状态 */
1038
+ bindingStatus: z7.number().int().optional(),
1039
+ /** 车牌 */
1040
+ plateNumber: z7.string().min(1).optional(),
1041
+ /** 1=在场缴费,2=逃费/欠费 */
1042
+ orderType: z7.number().int(),
1043
+ /** 车场侧订单号 */
1044
+ spParkingOrderId: z7.string().min(1).optional(),
1045
+ /** 车场 ID(协议定 string) */
1046
+ parkingId: z7.string().min(1),
1047
+ /** 入场时间 */
1048
+ entranceTime: z7.string().min(1).optional(),
1049
+ /** 逗号分隔的能力枚举,如 "LEVEL_DISCOUNT,POINT_DISCOUNT,COUPON" */
1050
+ supportDiscount: z7.string().min(1).optional()
1051
+ });
1052
+ var parkingPaymentCardDataSchema = z7.object({
1053
+ quoteRef: z7.string().min(1),
1054
+ plateNo: z7.string().min(1),
1055
+ entryAtText: z7.string().optional(),
1056
+ durationText: z7.string().optional(),
1057
+ /** 费用明细(label + 金额字符串) */
1058
+ feeItems: z7.array(z7.object({ name: z7.string(), amountText: z7.string() })),
1059
+ totalText: z7.string(),
1060
+ discountText: z7.string().optional(),
1061
+ payableText: z7.string(),
1062
+ /** 当前请求主体的会员状态;旧历史卡可缺省。 */
1063
+ membership: z7.object({
1064
+ state: z7.enum(["unauthenticated", "unknown", "active", "not_enrolled", "closed", "unavailable"]),
1065
+ availablePoints: z7.number().int().min(0).optional()
1066
+ }).optional(),
1067
+ /** 服务端归一化后的支付入口;客户端不解析权益标题。 */
1068
+ paymentOptions: z7.object({
1069
+ direct: z7.object({
1070
+ /** 已应用积分时 Provider 未必能预览无积分金额;缺省时客户端不得提前承诺金额。 */
1071
+ payableText: z7.string().optional(),
1072
+ benefitRefs: z7.array(z7.string())
1073
+ }),
1074
+ points: z7.discriminatedUnion("kind", [
1075
+ z7.object({
1076
+ kind: z7.literal("exact"),
1077
+ pointsToUse: z7.number().int().positive(),
1078
+ payableText: z7.string(),
1079
+ benefitRefs: z7.array(z7.string()).min(1)
1080
+ }),
1081
+ z7.object({
1082
+ kind: z7.literal("requires_reprice"),
1083
+ title: z7.string().min(1),
1084
+ benefitRefs: z7.array(z7.string()).min(1)
1085
+ })
1086
+ ]).optional()
1087
+ }).optional(),
1088
+ /** 已生效权益 */
1089
+ appliedBenefits: z7.array(
1090
+ z7.object({
1091
+ benefitRef: z7.string(),
1092
+ title: z7.string(),
1093
+ discountText: z7.string().optional()
1094
+ })
1095
+ ),
1096
+ /** 可变更权益(reprice 候选;autoSelected 预勾选) */
1097
+ selectableBenefits: z7.array(
1098
+ z7.object({
1099
+ benefitRef: z7.string(),
1100
+ title: z7.string(),
1101
+ autoSelected: z7.boolean(),
1102
+ description: z7.string().optional(),
1103
+ /** 权益类型(券 / 积分抵扣 / 等级权益);客户端据此分组与判定互斥,不得从标题猜测 */
1104
+ kind: z7.enum(["points", "discount", "coupon", "other"]).optional(),
1105
+ /** 预估优惠金额文案(本地展示用;权威金额以 reprice 回填为准) */
1106
+ discountText: z7.string().optional(),
1107
+ /**
1108
+ * 与本权益互斥的类型。三类优惠默认可叠加,互斥是厂商按券配置的例外——
1109
+ * 标记 ['points'] 表示该券不可与积分抵扣叠加。判定双向生效。
1110
+ */
1111
+ exclusiveWithKinds: z7.array(z7.enum(["points", "discount", "coupon"])).optional()
1112
+ })
1113
+ ),
1114
+ /** 乐观锁(提交校验;过期 → 提示重新查询) */
1115
+ revision: z7.number().int().min(1),
1116
+ caveats: z7.array(z7.string()),
1117
+ /**
1118
+ * 支付交接包:客户端卡据 kind 调壳 tbox 能力收款——
1119
+ * kind:'pay' → openParkingPay(传 orderInfo,壳跳确认页/收银台);kind:'unpaid' → openParkingUnpaidOrder(传 orderNo)。
1120
+ * 缺省 = 走内部 confirm-payment(line)支付链。
1121
+ */
1122
+ payHandoff: z7.object({
1123
+ kind: z7.enum(["pay", "unpaid"]),
1124
+ parkingStoreId: z7.string().min(1).optional(),
1125
+ orderInfo: parkingOrderInfoSchema.optional(),
1126
+ orderNo: z7.string().min(1).optional()
1127
+ }).optional(),
1128
+ /**
1129
+ * 支付进度(缺省 = 未发起):一经写入这张报价单即终结,按钮恒置灰。
1130
+ * 报价是一次性的(token singleUse + revision 乐观锁),发起支付后不可再提交。
1131
+ */
1132
+ settlement: z7.object({
1133
+ state: z7.enum(["paying", "paid", "failed"]),
1134
+ text: z7.string().min(1)
1135
+ }).optional()
1136
+ });
1137
+ var parkingPaymentResultCardDataSchema = z7.object({
1138
+ paymentRef: z7.string().min(1),
1139
+ plateNo: z7.string(),
1140
+ status: z7.enum(["PAID", "CANCELLED", "UNKNOWN"]),
1141
+ amountText: z7.string().optional(),
1142
+ message: z7.string(),
1143
+ /** 失败/取消:可重新发起(重新查询报价) */
1144
+ retryable: z7.boolean().optional(),
1145
+ /** 离场时限提醒(PAID:缴费后免费离场窗口,超时重新计费) */
1146
+ leaveNotice: z7.string().optional(),
1147
+ /** 回执明细(车牌 / 停车时长 / 缴费时间等,服务端已格式化) */
1148
+ details: z7.array(z7.object({ label: z7.string(), value: z7.string() })).optional()
1149
+ });
1150
+ var parkingPaymentRecordsCardDataSchema = z7.object({
1151
+ items: z7.array(
1152
+ z7.object({
1153
+ recordRef: z7.string().min(1),
1154
+ plateNo: z7.string(),
1155
+ amountText: z7.string(),
1156
+ paidAtText: z7.string(),
1157
+ status: z7.enum(["PAID", "REFUNDED", "CLOSED", "UNKNOWN"]),
1158
+ statusText: z7.string().optional(),
1159
+ parkName: z7.string().optional(),
1160
+ plazaName: z7.string().optional(),
1161
+ /** 展开态字段(entry/exit/单号脱敏) */
1162
+ entryAtText: z7.string().optional(),
1163
+ exitAtText: z7.string().optional(),
1164
+ payOrderNoMasked: z7.string().optional(),
1165
+ orderCompleted: z7.boolean().optional(),
1166
+ invoiceEligible: z7.boolean().optional(),
1167
+ invoiceIssued: z7.boolean().optional()
1168
+ })
1169
+ ),
1170
+ total: z7.number().int().min(0),
1171
+ hasMore: z7.boolean(),
1172
+ /** 厂商口径 caveats(厂商事实,7 条并入) */
1173
+ caveats: z7.array(z7.string()),
1174
+ /** 详情加载、开票提交等动作反馈 */
1175
+ message: z7.string().optional()
1176
+ });
1177
+ var parkingVehicleManagementCardDataSchema = z7.object({
1178
+ bound: z7.array(z7.object({
1179
+ vehicleRef: z7.string(),
1180
+ plateNo: z7.string(),
1181
+ parked: z7.boolean().optional()
1182
+ })),
1183
+ message: z7.string().optional(),
1184
+ /**
1185
+ * 会员状态(缺省 = 未探测):仅 not_enrolled 才展示入会引导。
1186
+ * 与 parking-payment 的 membership 同源,避免两张卡对同一用户给出不同判断。
1187
+ */
1188
+ membership: z7.object({
1189
+ state: z7.enum(["unauthenticated", "unknown", "active", "not_enrolled", "closed", "unavailable"])
1190
+ }).optional()
1191
+ });
1192
+ var PARKING_PLATE_PATTERN = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-HJ-NP-Z][A-HJ-NP-Z](?:[A-HJ-NP-Z0-9]{6}|[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳])$/;
1193
+ var parkingInfoCardDataSchema = z7.object({
1194
+ kind: z7.enum(["chargeRules", "benefitPolicy", "vehicleLocation"]),
1195
+ title: z7.string().min(1),
1196
+ entries: z7.array(z7.object({ label: z7.string(), value: z7.string() }))
1197
+ });
1198
+ var parkingCardDataSchemas = {
1199
+ "parking-payment": parkingPaymentCardDataSchema,
1200
+ "parking-payment-result": parkingPaymentResultCardDataSchema,
1201
+ "parking-payment-records": parkingPaymentRecordsCardDataSchema,
1202
+ "parking-vehicle-management": parkingVehicleManagementCardDataSchema,
1203
+ "parking-info": parkingInfoCardDataSchema
413
1204
  };
414
1205
  export {
1206
+ AUTH_LOGIN_SERVICE,
1207
+ MALL_INFO_CACHE_SERVICE,
1208
+ MALL_INFO_SERVICE,
415
1209
  MALL_SCOPE_KEY,
1210
+ MEMBER_ACCOUNT_SERVICE,
1211
+ MEMBER_BENEFITS_SERVICE,
1212
+ MEMBER_CARD_SERVICE,
1213
+ MEMBER_ENROLLMENT_SERVICE,
1214
+ MEMBER_PREFERENCE_PROMPTS,
1215
+ MEMBER_PREFERENCE_RECOMMENDATION_QUERIES,
1216
+ MEMBER_PREFERENCE_VALUES,
1217
+ MEMBER_PROFILE_SERVICE,
1218
+ MEMBER_SNAPSHOT_SERVICE,
1219
+ PARKING_PAYMENT_SERVICE,
1220
+ PARKING_PLATE_PATTERN,
1221
+ PARKING_QUERY_SERVICE,
1222
+ PARKING_SNAPSHOT_SERVICE,
1223
+ PARKING_VEHICLE_SERVICE,
1224
+ PROMOTION_ACQUISITION_SERVICE,
1225
+ PROMOTION_ACTIVITIES_SERVICE,
1226
+ PROMOTION_ACTIVITY_ROUTING_POLICY,
1227
+ PROMOTION_ENTITLEMENTS_SERVICE,
1228
+ PROMOTION_OFFERS_SERVICE,
1229
+ SHOPPING_CATALOG_SERVICE,
1230
+ SHOPPING_CATEGORIES_SERVICE,
1231
+ SHOPPING_DIRECTORY_SERVICE,
1232
+ SHOPPING_SHOPS_SERVICE,
416
1233
  actionDraftSchema,
417
1234
  categoryListCardDataSchema,
1235
+ classifyMemberOutcome,
1236
+ createMallContextValidator,
418
1237
  createMallRuntimeAssembly,
419
1238
  customerServiceFaqCardDataSchema,
420
1239
  evidenceSchema,
1240
+ externalMemberRefOf,
421
1241
  extractMallScope,
422
1242
  httpsUrlSchema,
423
1243
  humanGuideListCardDataSchema,
@@ -425,16 +1245,23 @@ export {
425
1245
  locationHandoffCardDataSchema,
426
1246
  mallDirectoryCardDataSchema,
427
1247
  mallLocationSchema,
1248
+ mallScopeOf,
1249
+ mallScopeOrEmpty,
428
1250
  mallScopeSchema,
1251
+ mallSlot,
1252
+ mallSubjectIdOf,
429
1253
  mallUserRefSchema,
1254
+ memberAuthStateSchema,
430
1255
  memberCardDataSchemas,
431
1256
  memberCardSummaryCardDataSchema,
432
- memberCouponDetailCardDataSchema,
433
- memberCouponListCardDataSchema,
1257
+ memberEnrollmentCtaCardDataSchema,
434
1258
  memberEnrollmentFormCardDataSchema,
435
1259
  memberEnrollmentStatusCardDataSchema,
1260
+ memberGateCardOf,
436
1261
  memberInfoCardDataSchema,
1262
+ memberLoginCtaCardDataSchema,
437
1263
  memberPointUsageListCardDataSchema,
1264
+ memberPreferenceValueSchema,
438
1265
  memberStateSchema,
439
1266
  merchantDetailCardDataSchema,
440
1267
  merchantListCardDataSchema,
@@ -443,6 +1270,13 @@ export {
443
1270
  moneySchema,
444
1271
  nearbyMallListCardDataSchema,
445
1272
  pageInfoSchema,
1273
+ parkingCardDataSchemas,
1274
+ parkingInfoCardDataSchema,
1275
+ parkingOrderInfoSchema,
1276
+ parkingPaymentCardDataSchema,
1277
+ parkingPaymentRecordsCardDataSchema,
1278
+ parkingPaymentResultCardDataSchema,
1279
+ parkingVehicleManagementCardDataSchema,
446
1280
  parseMallLocation,
447
1281
  parseMallScope,
448
1282
  parseMallUserRef,
@@ -450,8 +1284,28 @@ export {
450
1284
  productComparisonCardDataSchema,
451
1285
  productDetailCardDataSchema,
452
1286
  productListCardDataSchema,
1287
+ promotionActivityCategoryListCardDataSchema,
1288
+ promotionActivityDetailCardDataSchema,
1289
+ promotionActivityListCardDataSchema,
1290
+ promotionBestDealCardDataSchema,
1291
+ promotionBumpInActivityCardDataSchema,
1292
+ promotionCardDataSchemas,
1293
+ promotionEntitlementListCardDataSchema,
1294
+ promotionOfferDetailCardDataSchema,
1295
+ promotionOfferListCardDataSchema,
1296
+ readLoginMallId,
1297
+ readMallIdFromUrl,
1298
+ readMiniAppIdFromUrl,
453
1299
  reasonItemSchema,
1300
+ recommendMerchantsCardDataSchema,
454
1301
  refinementSchema,
455
- requireMallScope,
456
- shoppingGuideCardDataSchemas
1302
+ resolveMemberGate,
1303
+ shopDetailCardDataSchema,
1304
+ shopListCardDataSchema,
1305
+ shopLocationSchema,
1306
+ shopStatusSchema,
1307
+ shoppingGuideCardDataSchemas,
1308
+ toMallQueryContext,
1309
+ withMallName,
1310
+ withMallScopeFromQuery
457
1311
  };