@youzan-cloud/zan-saas-cli 0.0.0-stage → 0.1.0-beta.4

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 (329) hide show
  1. package/DEPENDENCIES.json +50 -0
  2. package/INSTALL.md +79 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE +1 -0
  5. package/README.md +151 -2
  6. package/THIRD_PARTY_NOTICES.txt +162 -0
  7. package/bin/zan-saas-cli.js +28 -0
  8. package/internal/registry/index.js +123 -0
  9. package/internal/registry/manifest.json +424 -0
  10. package/npm-shrinkwrap.json +86 -0
  11. package/package.json +33 -4
  12. package/skills/README.md +30 -0
  13. package/skills/zan-saas/SKILL.md +54 -0
  14. package/skills/zan-saas/references/host-entry.md +25 -0
  15. package/skills/zan-saas-access/SKILL.md +37 -0
  16. package/skills/zan-saas-access/references/role-list.md +11 -0
  17. package/skills/zan-saas-access/references/staff-perms-get.md +11 -0
  18. package/skills/zan-saas-aftersale/SKILL.md +31 -0
  19. package/skills/zan-saas-aftersale/references/case-list.md +13 -0
  20. package/skills/zan-saas-aftersale/references/case-scan.md +31 -0
  21. package/skills/zan-saas-benefit/SKILL.md +46 -0
  22. package/skills/zan-saas-benefit/references/card-get.md +7 -0
  23. package/skills/zan-saas-benefit/references/card-grant-list.md +11 -0
  24. package/skills/zan-saas-benefit/references/card-list.md +7 -0
  25. package/skills/zan-saas-benefit/references/customer-card-get.md +7 -0
  26. package/skills/zan-saas-benefit/references/customer-card-list.md +7 -0
  27. package/skills/zan-saas-coupon/SKILL.md +41 -0
  28. package/skills/zan-saas-coupon/references/activity-get.md +11 -0
  29. package/skills/zan-saas-coupon/references/activity-list.md +15 -0
  30. package/skills/zan-saas-coupon/references/voucher-get.md +13 -0
  31. package/skills/zan-saas-coupon/references/voucher-list.md +25 -0
  32. package/skills/zan-saas-customer/SKILL.md +73 -0
  33. package/skills/zan-saas-customer/references/customer-scan.md +11 -0
  34. package/skills/zan-saas-customer/references/segment-list.md +22 -0
  35. package/skills/zan-saas-fulfillment/SKILL.md +36 -0
  36. package/skills/zan-saas-fulfillment/references/delivery-get.md +11 -0
  37. package/skills/zan-saas-fulfillment/references/shipment-list.md +11 -0
  38. package/skills/zan-saas-fulfillment/references/waybill-get.md +9 -0
  39. package/skills/zan-saas-interaction/SKILL.md +37 -0
  40. package/skills/zan-saas-interaction/references/lottery-get.md +11 -0
  41. package/skills/zan-saas-interaction/references/lottery-list.md +19 -0
  42. package/skills/zan-saas-item/SKILL.md +61 -0
  43. package/skills/zan-saas-item/references/attribute-list.md +11 -0
  44. package/skills/zan-saas-item/references/brand-list.md +7 -0
  45. package/skills/zan-saas-item/references/classification-get.md +7 -0
  46. package/skills/zan-saas-item/references/classification-list.md +7 -0
  47. package/skills/zan-saas-item/references/group-list.md +9 -0
  48. package/skills/zan-saas-item/references/item-content.md +9 -0
  49. package/skills/zan-saas-item/references/item-get.md +9 -0
  50. package/skills/zan-saas-item/references/item-search.md +23 -0
  51. package/skills/zan-saas-item/references/product-identity.md +11 -0
  52. package/skills/zan-saas-member/SKILL.md +41 -0
  53. package/skills/zan-saas-member/references/customer-level-get.md +7 -0
  54. package/skills/zan-saas-member/references/growth-log-list.md +17 -0
  55. package/skills/zan-saas-member/references/membership-get.md +7 -0
  56. package/skills/zan-saas-order/SKILL.md +56 -0
  57. package/skills/zan-saas-order/references/order-get.md +26 -0
  58. package/skills/zan-saas-order/references/order-list.md +25 -0
  59. package/skills/zan-saas-order/references/order-scan.md +35 -0
  60. package/skills/zan-saas-org/SKILL.md +37 -0
  61. package/skills/zan-saas-org/references/staff-get.md +15 -0
  62. package/skills/zan-saas-org/references/staff-role-get.md +11 -0
  63. package/skills/zan-saas-org/references/unit-get.md +11 -0
  64. package/skills/zan-saas-org/references/unit-list.md +15 -0
  65. package/skills/zan-saas-points/SKILL.md +42 -0
  66. package/skills/zan-saas-points/references/account-get.md +7 -0
  67. package/skills/zan-saas-points/references/log-list.md +17 -0
  68. package/skills/zan-saas-points/references/rule-list.md +2 -0
  69. package/skills/zan-saas-privacy/SKILL.md +30 -0
  70. package/skills/zan-saas-privacy/references/field-decrypt.md +19 -0
  71. package/skills/zan-saas-shared/SKILL.md +94 -0
  72. package/skills/zan-saas-shared/references/authentication.md +52 -0
  73. package/skills/zan-saas-shared/references/value-processing.md +27 -0
  74. package/skills/zan-saas-stock/SKILL.md +28 -0
  75. package/skills/zan-saas-stock/references/log-list.md +11 -0
  76. package/skills/zan-saas-valuecard/SKILL.md +37 -0
  77. package/skills/zan-saas-valuecard/references/card-get.md +13 -0
  78. package/skills/zan-saas-valuecard/references/card-list.md +11 -0
  79. package/src/auth/browser.js +28 -0
  80. package/src/auth/callback-server.js +99 -0
  81. package/src/auth/config.js +53 -0
  82. package/src/auth/credential-store.js +109 -0
  83. package/src/auth/oauth-client.js +105 -0
  84. package/src/auth/pkce.js +28 -0
  85. package/src/auth/session.js +178 -0
  86. package/src/auth/tool.js +25 -0
  87. package/src/cli/command-paths.js +126 -0
  88. package/src/cli/context-help.js +102 -0
  89. package/src/cli/diagnostic-command.js +57 -0
  90. package/src/cli/domain-catalog.js +42 -0
  91. package/src/cli/help-text.js +32 -0
  92. package/src/cli/native-loader.js +120 -0
  93. package/src/cli/native-path.js +22 -0
  94. package/src/cli/parser-policy.js +141 -0
  95. package/src/cli/program.js +134 -0
  96. package/src/cli/skill-catalog.js +241 -0
  97. package/src/commands/native/access/commands.index.json +24 -0
  98. package/src/commands/native/access/domain.json +3 -0
  99. package/src/commands/native/access/role/list/command.js +141 -0
  100. package/src/commands/native/access/role/list/schema.json +393 -0
  101. package/src/commands/native/access/staff-perms/get/command.js +119 -0
  102. package/src/commands/native/access/staff-perms/get/schema.json +703 -0
  103. package/src/commands/native/aftersale/case/get/command.js +104 -0
  104. package/src/commands/native/aftersale/case/get/exact-json.js +52 -0
  105. package/src/commands/native/aftersale/case/get/money.js +28 -0
  106. package/src/commands/native/aftersale/case/get/projection.js +120 -0
  107. package/src/commands/native/aftersale/case/get/query.js +72 -0
  108. package/src/commands/native/aftersale/case/get/schema.json +134 -0
  109. package/src/commands/native/aftersale/case/list/command.js +135 -0
  110. package/src/commands/native/aftersale/case/list/schema.json +1095 -0
  111. package/src/commands/native/aftersale/case/scan/command.js +110 -0
  112. package/src/commands/native/aftersale/case/scan/query.js +62 -0
  113. package/src/commands/native/aftersale/case/scan/scan.js +78 -0
  114. package/src/commands/native/aftersale/case/scan/schema.json +964 -0
  115. package/src/commands/native/aftersale/case/scan/search.js +108 -0
  116. package/src/commands/native/aftersale/commands.index.json +42 -0
  117. package/src/commands/native/aftersale/domain.json +3 -0
  118. package/src/commands/native/aftersale/refund/list/command.js +97 -0
  119. package/src/commands/native/aftersale/refund/list/money.js +25 -0
  120. package/src/commands/native/aftersale/refund/list/projection.js +50 -0
  121. package/src/commands/native/aftersale/refund/list/query.js +73 -0
  122. package/src/commands/native/aftersale/refund/list/schema.json +130 -0
  123. package/src/commands/native/aftersale/shared/query.js +58 -0
  124. package/src/commands/native/aftersale/shared/search.js +128 -0
  125. package/src/commands/native/benefit/card-definition/get/command.js +110 -0
  126. package/src/commands/native/benefit/card-definition/get/schema.json +545 -0
  127. package/src/commands/native/benefit/card-definition/list/command.js +106 -0
  128. package/src/commands/native/benefit/card-definition/list/schema.json +690 -0
  129. package/src/commands/native/benefit/card-grant/list/command.js +135 -0
  130. package/src/commands/native/benefit/card-grant/list/schema.json +758 -0
  131. package/src/commands/native/benefit/commands.index.json +51 -0
  132. package/src/commands/native/benefit/customer-card/get/command.js +110 -0
  133. package/src/commands/native/benefit/customer-card/get/schema.json +340 -0
  134. package/src/commands/native/benefit/customer-card/list/command.js +103 -0
  135. package/src/commands/native/benefit/customer-card/list/schema.json +731 -0
  136. package/src/commands/native/benefit/domain.json +3 -0
  137. package/src/commands/native/coupon/activity/get/command.js +183 -0
  138. package/src/commands/native/coupon/activity/get/schema.json +624 -0
  139. package/src/commands/native/coupon/activity/list/command.js +169 -0
  140. package/src/commands/native/coupon/activity/list/schema.json +845 -0
  141. package/src/commands/native/coupon/commands.index.json +42 -0
  142. package/src/commands/native/coupon/domain.json +3 -0
  143. package/src/commands/native/coupon/shared/query.js +15 -0
  144. package/src/commands/native/coupon/voucher/get/command.js +113 -0
  145. package/src/commands/native/coupon/voucher/get/schema.json +147 -0
  146. package/src/commands/native/coupon/voucher/list/command.js +155 -0
  147. package/src/commands/native/coupon/voucher/list/schema.json +772 -0
  148. package/src/commands/native/customer/commands.index.json +96 -0
  149. package/src/commands/native/customer/customer/get/adapter.js +82 -0
  150. package/src/commands/native/customer/customer/get/command.js +106 -0
  151. package/src/commands/native/customer/customer/get/schema.json +958 -0
  152. package/src/commands/native/customer/customer/list/command.js +78 -0
  153. package/src/commands/native/customer/customer/list/input.js +145 -0
  154. package/src/commands/native/customer/customer/list/query.js +103 -0
  155. package/src/commands/native/customer/customer/list/schema.json +969 -0
  156. package/src/commands/native/customer/customer/scan/command.js +80 -0
  157. package/src/commands/native/customer/customer/scan/scan.js +92 -0
  158. package/src/commands/native/customer/customer/scan/schema.json +211 -0
  159. package/src/commands/native/customer/customer-tag/get/command.js +166 -0
  160. package/src/commands/native/customer/customer-tag/get/schema.json +317 -0
  161. package/src/commands/native/customer/customer-tag/list/command.js +143 -0
  162. package/src/commands/native/customer/customer-tag/list/schema.json +259 -0
  163. package/src/commands/native/customer/domain.json +3 -0
  164. package/src/commands/native/customer/segment/list/command.js +96 -0
  165. package/src/commands/native/customer/segment/list/query.js +98 -0
  166. package/src/commands/native/customer/segment/list/schema.json +135 -0
  167. package/src/commands/native/customer/segment-customer/list/command.js +171 -0
  168. package/src/commands/native/customer/segment-customer/list/schema.json +123 -0
  169. package/src/commands/native/customer/shared/search-input.js +179 -0
  170. package/src/commands/native/customer/shared/search-page.js +54 -0
  171. package/src/commands/native/customer/tag/get/command.js +73 -0
  172. package/src/commands/native/customer/tag/get/schema.json +339 -0
  173. package/src/commands/native/customer/tagcategory/get/command.js +80 -0
  174. package/src/commands/native/customer/tagcategory/get/query.js +98 -0
  175. package/src/commands/native/customer/tagcategory/get/schema.json +339 -0
  176. package/src/commands/native/customer/tagcategory/list/command.js +127 -0
  177. package/src/commands/native/customer/tagcategory/list/schema.json +394 -0
  178. package/src/commands/native/fulfillment/commands.index.json +33 -0
  179. package/src/commands/native/fulfillment/delivery/get/command.js +107 -0
  180. package/src/commands/native/fulfillment/delivery/get/projection.js +142 -0
  181. package/src/commands/native/fulfillment/delivery/get/schema.json +173 -0
  182. package/src/commands/native/fulfillment/domain.json +3 -0
  183. package/src/commands/native/fulfillment/shipment/list/command.js +83 -0
  184. package/src/commands/native/fulfillment/shipment/list/projection.js +130 -0
  185. package/src/commands/native/fulfillment/shipment/list/schema.json +752 -0
  186. package/src/commands/native/fulfillment/waybill/get/command.js +193 -0
  187. package/src/commands/native/fulfillment/waybill/get/response.js +68 -0
  188. package/src/commands/native/fulfillment/waybill/get/schema.json +190 -0
  189. package/src/commands/native/interaction/commands.index.json +24 -0
  190. package/src/commands/native/interaction/domain.json +3 -0
  191. package/src/commands/native/interaction/lottery/get/command.js +61 -0
  192. package/src/commands/native/interaction/lottery/get/query.js +152 -0
  193. package/src/commands/native/interaction/lottery/get/schema.json +1004 -0
  194. package/src/commands/native/interaction/lottery/list/command.js +176 -0
  195. package/src/commands/native/interaction/lottery/list/schema.json +898 -0
  196. package/src/commands/native/item/attribute/list/command.js +122 -0
  197. package/src/commands/native/item/attribute/list/schema.json +727 -0
  198. package/src/commands/native/item/brand/list/command.js +114 -0
  199. package/src/commands/native/item/brand/list/schema.json +725 -0
  200. package/src/commands/native/item/classification/get/command.js +87 -0
  201. package/src/commands/native/item/classification/get/schema.json +596 -0
  202. package/src/commands/native/item/classification/list/command.js +105 -0
  203. package/src/commands/native/item/classification/list/schema.json +676 -0
  204. package/src/commands/native/item/commands.index.json +87 -0
  205. package/src/commands/native/item/domain.json +1 -0
  206. package/src/commands/native/item/group/list/command.js +122 -0
  207. package/src/commands/native/item/group/list/schema.json +809 -0
  208. package/src/commands/native/item/item/get/command.js +122 -0
  209. package/src/commands/native/item/item/get/schema.json +3799 -0
  210. package/src/commands/native/item/item/list/command.js +7 -0
  211. package/src/commands/native/item/item/list/schema.json +1118 -0
  212. package/src/commands/native/item/item/scan/command.js +7 -0
  213. package/src/commands/native/item/item/scan/schema.json +1046 -0
  214. package/src/commands/native/item/item-content/get/command.js +54 -0
  215. package/src/commands/native/item/item-content/get/schema.json +677 -0
  216. package/src/commands/native/item/shared/api.js +31 -0
  217. package/src/commands/native/item/shared/content.js +20 -0
  218. package/src/commands/native/item/shared/diagnostics.js +61 -0
  219. package/src/commands/native/item/shared/h5.js +63 -0
  220. package/src/commands/native/item/shared/identity.js +62 -0
  221. package/src/commands/native/item/shared/normalize.js +49 -0
  222. package/src/commands/native/item/shared/scope.js +22 -0
  223. package/src/commands/native/item/shared/search-command.js +61 -0
  224. package/src/commands/native/item/shared/search-options.js +150 -0
  225. package/src/commands/native/item/shared/search.js +158 -0
  226. package/src/commands/native/member/commands.index.json +60 -0
  227. package/src/commands/native/member/customer-level/get/command.js +122 -0
  228. package/src/commands/native/member/customer-level/get/schema.json +300 -0
  229. package/src/commands/native/member/domain.json +3 -0
  230. package/src/commands/native/member/growth/get/command.js +64 -0
  231. package/src/commands/native/member/growth/get/schema.json +283 -0
  232. package/src/commands/native/member/growth-log/list/command.js +203 -0
  233. package/src/commands/native/member/growth-log/list/schema.json +695 -0
  234. package/src/commands/native/member/level/get/command.js +70 -0
  235. package/src/commands/native/member/level/get/query.js +125 -0
  236. package/src/commands/native/member/level/get/schema.json +339 -0
  237. package/src/commands/native/member/level/list/command.js +50 -0
  238. package/src/commands/native/member/level/list/query.js +109 -0
  239. package/src/commands/native/member/level/list/schema.json +104 -0
  240. package/src/commands/native/member/membership/get/command.js +78 -0
  241. package/src/commands/native/member/membership/get/schema.json +283 -0
  242. package/src/commands/native/order/commands.index.json +33 -0
  243. package/src/commands/native/order/domain.json +3 -0
  244. package/src/commands/native/order/order/get/command.js +155 -0
  245. package/src/commands/native/order/order/get/schema.json +1167 -0
  246. package/src/commands/native/order/order/list/command.js +230 -0
  247. package/src/commands/native/order/order/list/schema.json +1363 -0
  248. package/src/commands/native/order/order/scan/command.js +148 -0
  249. package/src/commands/native/order/order/scan/scan.js +89 -0
  250. package/src/commands/native/order/order/scan/schema.json +1445 -0
  251. package/src/commands/native/order/shared/projection.js +183 -0
  252. package/src/commands/native/order/shared/query.js +84 -0
  253. package/src/commands/native/order/shared/search.js +112 -0
  254. package/src/commands/native/org/commands.index.json +42 -0
  255. package/src/commands/native/org/domain.json +3 -0
  256. package/src/commands/native/org/shared/single-staff.js +115 -0
  257. package/src/commands/native/org/staff/get/command.js +148 -0
  258. package/src/commands/native/org/staff/get/schema.json +114 -0
  259. package/src/commands/native/org/staff-role/get/command.js +45 -0
  260. package/src/commands/native/org/staff-role/get/schema.json +93 -0
  261. package/src/commands/native/org/unit/get/command.js +131 -0
  262. package/src/commands/native/org/unit/get/schema.json +478 -0
  263. package/src/commands/native/org/unit/list/command.js +187 -0
  264. package/src/commands/native/org/unit/list/schema.json +828 -0
  265. package/src/commands/native/points/account/get/command.js +65 -0
  266. package/src/commands/native/points/account/get/schema.json +303 -0
  267. package/src/commands/native/points/commands.index.json +33 -0
  268. package/src/commands/native/points/domain.json +3 -0
  269. package/src/commands/native/points/log/list/command.js +141 -0
  270. package/src/commands/native/points/log/list/schema.json +761 -0
  271. package/src/commands/native/points/rule/list/command.js +82 -0
  272. package/src/commands/native/points/rule/list/schema.json +294 -0
  273. package/src/commands/native/privacy/commands.index.json +15 -0
  274. package/src/commands/native/privacy/domain.json +3 -0
  275. package/src/commands/native/privacy/field/decrypt/command.js +185 -0
  276. package/src/commands/native/privacy/field/decrypt/schema.json +656 -0
  277. package/src/commands/native/privacy/shared/order-source.js +33 -0
  278. package/src/commands/native/stock/commands.index.json +15 -0
  279. package/src/commands/native/stock/domain.json +3 -0
  280. package/src/commands/native/stock/log/list/command.js +207 -0
  281. package/src/commands/native/stock/log/list/schema.json +922 -0
  282. package/src/commands/native/valuecard/card/get/command.js +107 -0
  283. package/src/commands/native/valuecard/card/get/schema.json +683 -0
  284. package/src/commands/native/valuecard/card/list/command.js +136 -0
  285. package/src/commands/native/valuecard/card/list/pagination.js +23 -0
  286. package/src/commands/native/valuecard/card/list/schema.json +780 -0
  287. package/src/commands/native/valuecard/commands.index.json +24 -0
  288. package/src/commands/native/valuecard/domain.json +3 -0
  289. package/src/commands/native/valuecard/shared/projection.js +57 -0
  290. package/src/commands/system/api.js +47 -0
  291. package/src/commands/system/auth.js +118 -0
  292. package/src/commands/system/contracts.js +373 -0
  293. package/src/commands/system/doctor.js +24 -0
  294. package/src/commands/system/install.js +41 -0
  295. package/src/commands/system/printer.js +16 -0
  296. package/src/commands/system/schema.js +162 -0
  297. package/src/commands/system/shop-guidance.js +9 -0
  298. package/src/commands/system/shop.js +237 -0
  299. package/src/commands/system/skills.js +49 -0
  300. package/src/execution/beijing-date-time.js +29 -0
  301. package/src/execution/beijing-time.js +21 -0
  302. package/src/execution/bounded-input.js +42 -0
  303. package/src/execution/command-io.js +137 -0
  304. package/src/execution/command-policy.js +41 -0
  305. package/src/execution/command-schema.js +131 -0
  306. package/src/execution/datetime.js +83 -0
  307. package/src/execution/error-details.js +399 -0
  308. package/src/execution/error-model.js +82 -0
  309. package/src/execution/exact-json.js +80 -0
  310. package/src/execution/list-total.js +10 -0
  311. package/src/execution/money.js +59 -0
  312. package/src/execution/parser-error.js +66 -0
  313. package/src/execution/privacy.js +197 -0
  314. package/src/execution/public-schema.js +52 -0
  315. package/src/execution/query-api.js +101 -0
  316. package/src/execution/range.js +84 -0
  317. package/src/execution/request-state.js +55 -0
  318. package/src/execution/schema-completeness.js +116 -0
  319. package/src/execution/shop-query.js +54 -0
  320. package/src/execution/target-shop-default.js +23 -0
  321. package/src/execution/target-shop.js +49 -0
  322. package/src/identity/context-store.js +54 -0
  323. package/src/identity/context.js +38 -0
  324. package/src/installation/install.js +158 -0
  325. package/src/installation/npm-error.js +93 -0
  326. package/src/installation/skill.js +113 -0
  327. package/src/transport/account-api.js +152 -0
  328. package/src/transport/api.js +199 -0
  329. package/src/transport/shop-app.js +63 -0
@@ -0,0 +1,137 @@
1
+ 'use strict';
2
+
3
+ const { inputIssue } = require('./error-details');
4
+
5
+ /**
6
+ * Shared CLI input parsing, response mapping and module validation.
7
+ *
8
+ * Default rules:
9
+ * - Base result is fixed for each command
10
+ * - Additional modules via --include when needed (cost, auth, volume differ)
11
+ * - Success requires all base + requested modules fulfilled
12
+ * - Structured query output: { data: {...} }
13
+ * - Unpromised modules omitted; requested must appear on success
14
+ * - null = queried and confirmed no value/no association
15
+ * - [] = successfully retrieved empty collection within declared scope
16
+ * - Empty string reserved for empty text; 0 and false preserved
17
+ */
18
+
19
+ /**
20
+ * Parse --include parameter into normalized module set.
21
+ * @param {string} includeValue - comma-separated module names
22
+ * @param {string[]} allowedModules - valid module names for this command
23
+ * @returns {Set<string>} deduplicated, validated modules
24
+ */
25
+ function parseIncludeModules(includeValue, allowedModules, diagnosticPath) {
26
+ function reject(message, reason = 'invalid_format') {
27
+ if (diagnosticPath) throw inputIssue(diagnosticPath, reason, {
28
+ type: 'array', items: { enum: allowedModules }
29
+ });
30
+ throw new Error(message);
31
+ }
32
+ if (includeValue === undefined) return new Set();
33
+ if (typeof includeValue !== 'string') reject('--include must be text');
34
+
35
+ const modules = includeValue
36
+ .split(',')
37
+ .map(m => m.trim());
38
+
39
+ if (modules.some(module => !module)) {
40
+ reject('--include must not contain empty values or module names');
41
+ }
42
+
43
+ const moduleSet = new Set(modules);
44
+ const allowedSet = new Set(allowedModules);
45
+ const invalid = [...moduleSet].filter(m => !allowedSet.has(m));
46
+
47
+ if (invalid.length > 0) {
48
+ reject(
49
+ `Unsupported --include modules: ${invalid.join(', ')}. ` +
50
+ `Allowed: ${allowedModules.join(', ')}`, 'invalid_choice'
51
+ );
52
+ }
53
+
54
+ return moduleSet;
55
+ }
56
+
57
+ /**
58
+ * Validate that requested modules are present in adapted result.
59
+ * @param {Object} adaptedData - data after upstream adaptation
60
+ * @param {Set<string>} requestedModules - modules that were requested
61
+ * @param {string[]} baseModules - modules included by default
62
+ * @throws {Error} if any required module is missing
63
+ */
64
+ function validateRequiredModules(adaptedData, requestedModules, baseModules) {
65
+ const allRequired = new Set([...baseModules, ...requestedModules]);
66
+ const missing = [];
67
+
68
+ for (const module of allRequired) {
69
+ if (!(module in adaptedData)) {
70
+ missing.push(module);
71
+ }
72
+ }
73
+
74
+ if (missing.length > 0) {
75
+ throw new Error(
76
+ `Required modules missing from result: ${missing.join(', ')}. ` +
77
+ `All base and requested modules must be fulfilled for success.`
78
+ );
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Adapt upstream response to unified model.
84
+ * Verifies structure and maps to expected modules.
85
+ * @param {Object} upstreamResult - raw upstream response
86
+ * @param {Object} moduleMapping - map of module names to upstream paths
87
+ * @param {Set<string>} requestedModules - explicitly requested modules
88
+ * @param {string[]} baseModules - default included modules
89
+ * @returns {Object} adapted data keyed by module names
90
+ */
91
+ function adaptUpstreamToModel(upstreamResult, moduleMapping, requestedModules, baseModules) {
92
+ const adapted = {};
93
+ const allModules = new Set([...baseModules, ...requestedModules]);
94
+
95
+ for (const module of allModules) {
96
+ const mapping = moduleMapping[module];
97
+ if (!mapping) {
98
+ throw new Error(`No mapping defined for module: ${module}`);
99
+ }
100
+
101
+ const { upstreamPath, required } = mapping;
102
+ const value = getNestedValue(upstreamResult, upstreamPath);
103
+
104
+ if (value === undefined) {
105
+ if (required) {
106
+ throw new Error(
107
+ `Required module '${module}' not found at upstream path '${upstreamPath}'`
108
+ );
109
+ }
110
+ // Optional module not present - skip it (don't add to adapted)
111
+ continue;
112
+ }
113
+
114
+ adapted[module] = value;
115
+ }
116
+
117
+ return adapted;
118
+ }
119
+
120
+ /**
121
+ * Get nested value from object using dot-separated path.
122
+ * @param {Object} obj - source object
123
+ * @param {string} path - dot-separated path (e.g., "response.customer.tags")
124
+ * @returns {*} value at path, or undefined if not found
125
+ */
126
+ function getNestedValue(obj, path) {
127
+ return path.split('.').reduce((current, key) => {
128
+ return current?.[key];
129
+ }, obj);
130
+ }
131
+
132
+ module.exports = {
133
+ parseIncludeModules,
134
+ validateRequiredModules,
135
+ adaptUpstreamToModel,
136
+ getNestedValue
137
+ };
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ function validateAt(at) {
4
+ if (!['hq', 'shop', 'all'].includes(at)) {
5
+ throw new Error('Command at must be explicitly declared as hq, shop or all');
6
+ }
7
+ return at;
8
+ }
9
+
10
+ function isAvailable(at, context) {
11
+ return at === 'all' || at === context.orgType;
12
+ }
13
+
14
+ function assertAvailable(at, state, { dryRun = false, command, includeDetails = false,
15
+ contextStage = 'context' } = {}) {
16
+ function reject(message, category, reason) {
17
+ const error = new Error(message);
18
+ error.diagnosticReason = reason;
19
+ if (includeDetails) error.cliDetails = {
20
+ category, stage: contextStage, command, targetScope: {}, requestState: 'not_sent',
21
+ nextStep: state.status === 'missing'
22
+ ? '执行前需要登录并选择店铺:未登录先执行 `zan-saas auth login`;登录后执行 `zan-saas shop list` 和 `zan-saas shop use <kdtId>`。目标解析不证明凭证授权,不自动切换店铺或身份。'
23
+ : '检查当前用户上下文中的 userId、hq、shopId 和 orgType;不自动切换店铺或身份,权限由服务端校验。'
24
+ };
25
+ error.issues = [{ source: 'context', reason: 'constraint' }];
26
+ throw error;
27
+ }
28
+ if (state.status === 'missing' && dryRun) return;
29
+ if (state.status !== 'ready') {
30
+ reject(state.status === 'missing'
31
+ ? '执行前需要登录并选择店铺:未登录先执行 `zan-saas auth login`;登录后执行 `zan-saas shop list` 和 `zan-saas shop use <kdtId>`,也可以在受控环境提供 ZAN_SAAS_CONTEXT'
32
+ : '当前用户上下文无效,请检查 userId、hq、shopId 和 orgType', 'configuration', state.status === 'missing' ? 'context_missing' : 'context_invalid');
33
+ }
34
+ if (!isAvailable(at, state.context)) {
35
+ const position = state.context.orgType === 'hq' ? '总部' : '店铺';
36
+ const required = at === 'hq' ? '总部' : '店铺';
37
+ reject(`当前是${position}上下文,此命令只能在${required}上下文执行。`, 'permission', at === 'hq' ? 'requires_hq' : 'requires_shop');
38
+ }
39
+ }
40
+
41
+ module.exports = { validateAt, isAvailable, assertAvailable };
@@ -0,0 +1,131 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const Ajv = require('ajv');
5
+ const { validDateTime } = require('./datetime');
6
+ const { CommandValueError } = require('./error-details');
7
+ const { assertDiagnosticModel } = require('./error-model');
8
+
9
+ const ajv = new Ajv({ allErrors: true, strict: true });
10
+ ajv.addFormat('beijing-date-time', value => validDateTime(value));
11
+ ajv.addFormat('beijing-date-time-ms', value => validDateTime(value, true));
12
+ const compiled = new WeakMap();
13
+ const commandSchemas = new WeakMap();
14
+ const model = { type: 'object', minProperties: 1 };
15
+ const validateDefinition = ajv.compile({
16
+ type: 'object',
17
+ required: ['schemaVersion', 'input', 'output'],
18
+ additionalProperties: false,
19
+ properties: {
20
+ schemaVersion: { const: 1 },
21
+ input: model,
22
+ output: model,
23
+ preview: model,
24
+ error: model,
25
+ definitions: { type: 'object' },
26
+ modules: {
27
+ type: 'object',
28
+ minProperties: 1,
29
+ propertyNames: { pattern: '^[a-z][a-z0-9_-]*$' },
30
+ additionalProperties: {
31
+ type: 'object',
32
+ required: ['base', 'upstreamPath', 'required', 'description'],
33
+ additionalProperties: false,
34
+ properties: {
35
+ base: { type: 'boolean' },
36
+ upstreamPath: { type: 'string', minLength: 1 },
37
+ required: { type: 'boolean' },
38
+ description: { type: 'string', minLength: 1 }
39
+ }
40
+ }
41
+ },
42
+ api: {
43
+ type: 'object',
44
+ required: ['method', 'path'],
45
+ additionalProperties: false,
46
+ properties: {
47
+ method: { const: 'POST' },
48
+ path: { type: 'string', minLength: 1 }
49
+ }
50
+ },
51
+ success: { type: 'object' },
52
+ errors: { type: 'object' },
53
+ notes: { type: 'array', items: { type: 'string' } }
54
+ }
55
+ });
56
+
57
+ function validators(schema) {
58
+ if (compiled.has(schema)) return compiled.get(schema);
59
+ assertDiagnosticModel(schema);
60
+ if (!validateDefinition(schema)) {
61
+ throw new Error(`Invalid command schema: ${ajv.errorsText(validateDefinition.errors)}`);
62
+ }
63
+ const result = {};
64
+ for (const section of ['input', 'output', 'preview', 'error']) {
65
+ if (schema[section]) {
66
+ result[section] = ajv.compile({
67
+ ...schema[section],
68
+ definitions: schema.definitions || {}
69
+ });
70
+ }
71
+ }
72
+ compiled.set(schema, result);
73
+ return result;
74
+ }
75
+
76
+ function validateCommandValue(schema, section, value) {
77
+ const validate = validators(schema)[section];
78
+ if (!validate) throw new Error(`Command schema has no ${section} model`);
79
+ if (!validate(value)) {
80
+ throw new CommandValueError(section, structuredClone(validate.errors), schema);
81
+ }
82
+ }
83
+
84
+ function validateCommandSchema(schema) {
85
+ validators(schema);
86
+ }
87
+
88
+ function loadCommandSchema(filename) {
89
+ if (!fs.existsSync(filename) || !fs.lstatSync(filename).isFile()) {
90
+ throw new Error(`Missing command schema: ${filename}`);
91
+ }
92
+ const schema = JSON.parse(fs.readFileSync(filename, 'utf8'));
93
+ validators(schema);
94
+ return schema;
95
+ }
96
+
97
+ function attachCommandSchema(command, schema) {
98
+ validators(schema);
99
+ commandSchemas.set(command, schema);
100
+ }
101
+
102
+ function commandSchema(command) {
103
+ return commandSchemas.get(command);
104
+ }
105
+
106
+ function queryDefinition(schema) {
107
+ validators(schema);
108
+ if (!schema.modules || !schema.api) {
109
+ throw new Error('Query schema requires modules and api');
110
+ }
111
+ const baseModules = [];
112
+ const includeModules = [];
113
+ const mapping = {};
114
+ for (const [name, definition] of Object.entries(schema.modules)) {
115
+ (definition.base ? baseModules : includeModules).push(name);
116
+ mapping[name] = {
117
+ upstreamPath: definition.upstreamPath,
118
+ required: definition.required
119
+ };
120
+ }
121
+ return { baseModules, includeModules, mapping };
122
+ }
123
+
124
+ module.exports = {
125
+ loadCommandSchema,
126
+ attachCommandSchema,
127
+ commandSchema,
128
+ validateCommandSchema,
129
+ validateCommandValue,
130
+ queryDefinition
131
+ };
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const OFFSET_MS = 8 * 60 * 60 * 1000;
4
+ const INPUT_PATTERN = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{1,3})?$/;
5
+ const OUTPUT_PATTERN = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}$/;
6
+
7
+ function parseDateTime(value) {
8
+ if (typeof value !== 'string' || !INPUT_PATTERN.test(value.trim())) {
9
+ throw new Error('时间须为北京时间 YYYY-MM-DD HH:mm:ss,可追加 1 至 3 位毫秒');
10
+ }
11
+ const [calendar, fraction = ''] = value.trim().split('.');
12
+ const [year, month, day, hour, minute, second] = calendar.match(/\d+/g).map(Number);
13
+ const date = new Date(0);
14
+ date.setUTCFullYear(year, month - 1, day);
15
+ date.setUTCHours(hour, minute, second, Number(fraction.padEnd(3, '0')));
16
+ if (year < 1 || date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 ||
17
+ date.getUTCDate() !== day || date.getUTCHours() !== hour ||
18
+ date.getUTCMinutes() !== minute || date.getUTCSeconds() !== second) {
19
+ throw new Error('时间须为有效的实际日历时间');
20
+ }
21
+ return date.getTime() - OFFSET_MS;
22
+ }
23
+
24
+ function formatDateTime(milliseconds) {
25
+ if (!Number.isSafeInteger(milliseconds)) throw new Error('时间戳须为安全毫秒整数');
26
+ const date = new Date(milliseconds + OFFSET_MS);
27
+ const year = date.getUTCFullYear();
28
+ if (!Number.isFinite(date.getTime()) || year < 1 || year > 9999) {
29
+ throw new Error('时间超出 0001 至 9999 年的可表示范围');
30
+ }
31
+ return date.toISOString().slice(0, -1).replace('T', ' ');
32
+ }
33
+
34
+ function apiSeconds(value) {
35
+ const milliseconds = parseDateTime(value);
36
+ if (milliseconds % 1000 !== 0) throw new Error('此接口仅支持秒精度,不能传入非零毫秒');
37
+ return milliseconds / 1000;
38
+ }
39
+
40
+ function apiDateTime(value) {
41
+ return formatDateTime(apiSeconds(value) * 1000).slice(0, 19);
42
+ }
43
+
44
+ function validDateTime(value, output = false) {
45
+ try {
46
+ if (!(output ? OUTPUT_PATTERN : INPUT_PATTERN).test(value)) return false;
47
+ parseDateTime(value);
48
+ return true;
49
+ } catch { return false; }
50
+ }
51
+
52
+ function normalizeTimes(value, model, definitions = {}) {
53
+ if (model.$ref) {
54
+ return normalizeTimes(value, definitions[model.$ref.slice('#/definitions/'.length)], definitions);
55
+ }
56
+ if (value == null) return value;
57
+ if (model.$comment?.startsWith('upstream-time:')) {
58
+ const [, unit, ...sentinels] = model.$comment.split(':');
59
+ if ((value === '' && sentinels.includes('empty')) ||
60
+ (value === '永久有效' && sentinels.includes('permanent')) ||
61
+ (value === 0 && sentinels.includes('zero'))) return value;
62
+ if (unit === 'text') return formatDateTime(parseDateTime(value));
63
+ if (!Number.isSafeInteger(value)) throw new Error('上游时间戳须为安全整数');
64
+ if (unit === 'seconds') return formatDateTime(value * 1000);
65
+ if (unit === 'milliseconds') return formatDateTime(value);
66
+ throw new Error('未知的上游时间单位');
67
+ }
68
+ if (Array.isArray(value) && model.items) {
69
+ return value.map(entry => normalizeTimes(entry, model.items, definitions));
70
+ }
71
+ if (typeof value === 'object' && !Array.isArray(value) && model.properties) {
72
+ const result = { ...value };
73
+ for (const [key, child] of Object.entries(model.properties)) {
74
+ if (Object.hasOwn(value, key)) result[key] = normalizeTimes(value[key], child, definitions);
75
+ }
76
+ return result;
77
+ }
78
+ return value;
79
+ }
80
+
81
+ module.exports = {
82
+ parseDateTime, formatDateTime, apiSeconds, apiDateTime, validDateTime, normalizeTimes
83
+ };