@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.
- package/DEPENDENCIES.json +50 -0
- package/INSTALL.md +79 -0
- package/LICENSE +21 -0
- package/NOTICE +1 -0
- package/README.md +151 -2
- package/THIRD_PARTY_NOTICES.txt +162 -0
- package/bin/zan-saas-cli.js +28 -0
- package/internal/registry/index.js +123 -0
- package/internal/registry/manifest.json +424 -0
- package/npm-shrinkwrap.json +86 -0
- package/package.json +33 -4
- package/skills/README.md +30 -0
- package/skills/zan-saas/SKILL.md +54 -0
- package/skills/zan-saas/references/host-entry.md +25 -0
- package/skills/zan-saas-access/SKILL.md +37 -0
- package/skills/zan-saas-access/references/role-list.md +11 -0
- package/skills/zan-saas-access/references/staff-perms-get.md +11 -0
- package/skills/zan-saas-aftersale/SKILL.md +31 -0
- package/skills/zan-saas-aftersale/references/case-list.md +13 -0
- package/skills/zan-saas-aftersale/references/case-scan.md +31 -0
- package/skills/zan-saas-benefit/SKILL.md +46 -0
- package/skills/zan-saas-benefit/references/card-get.md +7 -0
- package/skills/zan-saas-benefit/references/card-grant-list.md +11 -0
- package/skills/zan-saas-benefit/references/card-list.md +7 -0
- package/skills/zan-saas-benefit/references/customer-card-get.md +7 -0
- package/skills/zan-saas-benefit/references/customer-card-list.md +7 -0
- package/skills/zan-saas-coupon/SKILL.md +41 -0
- package/skills/zan-saas-coupon/references/activity-get.md +11 -0
- package/skills/zan-saas-coupon/references/activity-list.md +15 -0
- package/skills/zan-saas-coupon/references/voucher-get.md +13 -0
- package/skills/zan-saas-coupon/references/voucher-list.md +25 -0
- package/skills/zan-saas-customer/SKILL.md +73 -0
- package/skills/zan-saas-customer/references/customer-scan.md +11 -0
- package/skills/zan-saas-customer/references/segment-list.md +22 -0
- package/skills/zan-saas-fulfillment/SKILL.md +36 -0
- package/skills/zan-saas-fulfillment/references/delivery-get.md +11 -0
- package/skills/zan-saas-fulfillment/references/shipment-list.md +11 -0
- package/skills/zan-saas-fulfillment/references/waybill-get.md +9 -0
- package/skills/zan-saas-interaction/SKILL.md +37 -0
- package/skills/zan-saas-interaction/references/lottery-get.md +11 -0
- package/skills/zan-saas-interaction/references/lottery-list.md +19 -0
- package/skills/zan-saas-item/SKILL.md +61 -0
- package/skills/zan-saas-item/references/attribute-list.md +11 -0
- package/skills/zan-saas-item/references/brand-list.md +7 -0
- package/skills/zan-saas-item/references/classification-get.md +7 -0
- package/skills/zan-saas-item/references/classification-list.md +7 -0
- package/skills/zan-saas-item/references/group-list.md +9 -0
- package/skills/zan-saas-item/references/item-content.md +9 -0
- package/skills/zan-saas-item/references/item-get.md +9 -0
- package/skills/zan-saas-item/references/item-search.md +23 -0
- package/skills/zan-saas-item/references/product-identity.md +11 -0
- package/skills/zan-saas-member/SKILL.md +41 -0
- package/skills/zan-saas-member/references/customer-level-get.md +7 -0
- package/skills/zan-saas-member/references/growth-log-list.md +17 -0
- package/skills/zan-saas-member/references/membership-get.md +7 -0
- package/skills/zan-saas-order/SKILL.md +56 -0
- package/skills/zan-saas-order/references/order-get.md +26 -0
- package/skills/zan-saas-order/references/order-list.md +25 -0
- package/skills/zan-saas-order/references/order-scan.md +35 -0
- package/skills/zan-saas-org/SKILL.md +37 -0
- package/skills/zan-saas-org/references/staff-get.md +15 -0
- package/skills/zan-saas-org/references/staff-role-get.md +11 -0
- package/skills/zan-saas-org/references/unit-get.md +11 -0
- package/skills/zan-saas-org/references/unit-list.md +15 -0
- package/skills/zan-saas-points/SKILL.md +42 -0
- package/skills/zan-saas-points/references/account-get.md +7 -0
- package/skills/zan-saas-points/references/log-list.md +17 -0
- package/skills/zan-saas-points/references/rule-list.md +2 -0
- package/skills/zan-saas-privacy/SKILL.md +30 -0
- package/skills/zan-saas-privacy/references/field-decrypt.md +19 -0
- package/skills/zan-saas-shared/SKILL.md +94 -0
- package/skills/zan-saas-shared/references/authentication.md +52 -0
- package/skills/zan-saas-shared/references/value-processing.md +27 -0
- package/skills/zan-saas-stock/SKILL.md +28 -0
- package/skills/zan-saas-stock/references/log-list.md +11 -0
- package/skills/zan-saas-valuecard/SKILL.md +37 -0
- package/skills/zan-saas-valuecard/references/card-get.md +13 -0
- package/skills/zan-saas-valuecard/references/card-list.md +11 -0
- package/src/auth/browser.js +28 -0
- package/src/auth/callback-server.js +99 -0
- package/src/auth/config.js +53 -0
- package/src/auth/credential-store.js +109 -0
- package/src/auth/oauth-client.js +105 -0
- package/src/auth/pkce.js +28 -0
- package/src/auth/session.js +178 -0
- package/src/auth/tool.js +25 -0
- package/src/cli/command-paths.js +126 -0
- package/src/cli/context-help.js +102 -0
- package/src/cli/diagnostic-command.js +57 -0
- package/src/cli/domain-catalog.js +42 -0
- package/src/cli/help-text.js +32 -0
- package/src/cli/native-loader.js +120 -0
- package/src/cli/native-path.js +22 -0
- package/src/cli/parser-policy.js +141 -0
- package/src/cli/program.js +134 -0
- package/src/cli/skill-catalog.js +241 -0
- package/src/commands/native/access/commands.index.json +24 -0
- package/src/commands/native/access/domain.json +3 -0
- package/src/commands/native/access/role/list/command.js +141 -0
- package/src/commands/native/access/role/list/schema.json +393 -0
- package/src/commands/native/access/staff-perms/get/command.js +119 -0
- package/src/commands/native/access/staff-perms/get/schema.json +703 -0
- package/src/commands/native/aftersale/case/get/command.js +104 -0
- package/src/commands/native/aftersale/case/get/exact-json.js +52 -0
- package/src/commands/native/aftersale/case/get/money.js +28 -0
- package/src/commands/native/aftersale/case/get/projection.js +120 -0
- package/src/commands/native/aftersale/case/get/query.js +72 -0
- package/src/commands/native/aftersale/case/get/schema.json +134 -0
- package/src/commands/native/aftersale/case/list/command.js +135 -0
- package/src/commands/native/aftersale/case/list/schema.json +1095 -0
- package/src/commands/native/aftersale/case/scan/command.js +110 -0
- package/src/commands/native/aftersale/case/scan/query.js +62 -0
- package/src/commands/native/aftersale/case/scan/scan.js +78 -0
- package/src/commands/native/aftersale/case/scan/schema.json +964 -0
- package/src/commands/native/aftersale/case/scan/search.js +108 -0
- package/src/commands/native/aftersale/commands.index.json +42 -0
- package/src/commands/native/aftersale/domain.json +3 -0
- package/src/commands/native/aftersale/refund/list/command.js +97 -0
- package/src/commands/native/aftersale/refund/list/money.js +25 -0
- package/src/commands/native/aftersale/refund/list/projection.js +50 -0
- package/src/commands/native/aftersale/refund/list/query.js +73 -0
- package/src/commands/native/aftersale/refund/list/schema.json +130 -0
- package/src/commands/native/aftersale/shared/query.js +58 -0
- package/src/commands/native/aftersale/shared/search.js +128 -0
- package/src/commands/native/benefit/card-definition/get/command.js +110 -0
- package/src/commands/native/benefit/card-definition/get/schema.json +545 -0
- package/src/commands/native/benefit/card-definition/list/command.js +106 -0
- package/src/commands/native/benefit/card-definition/list/schema.json +690 -0
- package/src/commands/native/benefit/card-grant/list/command.js +135 -0
- package/src/commands/native/benefit/card-grant/list/schema.json +758 -0
- package/src/commands/native/benefit/commands.index.json +51 -0
- package/src/commands/native/benefit/customer-card/get/command.js +110 -0
- package/src/commands/native/benefit/customer-card/get/schema.json +340 -0
- package/src/commands/native/benefit/customer-card/list/command.js +103 -0
- package/src/commands/native/benefit/customer-card/list/schema.json +731 -0
- package/src/commands/native/benefit/domain.json +3 -0
- package/src/commands/native/coupon/activity/get/command.js +183 -0
- package/src/commands/native/coupon/activity/get/schema.json +624 -0
- package/src/commands/native/coupon/activity/list/command.js +169 -0
- package/src/commands/native/coupon/activity/list/schema.json +845 -0
- package/src/commands/native/coupon/commands.index.json +42 -0
- package/src/commands/native/coupon/domain.json +3 -0
- package/src/commands/native/coupon/shared/query.js +15 -0
- package/src/commands/native/coupon/voucher/get/command.js +113 -0
- package/src/commands/native/coupon/voucher/get/schema.json +147 -0
- package/src/commands/native/coupon/voucher/list/command.js +155 -0
- package/src/commands/native/coupon/voucher/list/schema.json +772 -0
- package/src/commands/native/customer/commands.index.json +96 -0
- package/src/commands/native/customer/customer/get/adapter.js +82 -0
- package/src/commands/native/customer/customer/get/command.js +106 -0
- package/src/commands/native/customer/customer/get/schema.json +958 -0
- package/src/commands/native/customer/customer/list/command.js +78 -0
- package/src/commands/native/customer/customer/list/input.js +145 -0
- package/src/commands/native/customer/customer/list/query.js +103 -0
- package/src/commands/native/customer/customer/list/schema.json +969 -0
- package/src/commands/native/customer/customer/scan/command.js +80 -0
- package/src/commands/native/customer/customer/scan/scan.js +92 -0
- package/src/commands/native/customer/customer/scan/schema.json +211 -0
- package/src/commands/native/customer/customer-tag/get/command.js +166 -0
- package/src/commands/native/customer/customer-tag/get/schema.json +317 -0
- package/src/commands/native/customer/customer-tag/list/command.js +143 -0
- package/src/commands/native/customer/customer-tag/list/schema.json +259 -0
- package/src/commands/native/customer/domain.json +3 -0
- package/src/commands/native/customer/segment/list/command.js +96 -0
- package/src/commands/native/customer/segment/list/query.js +98 -0
- package/src/commands/native/customer/segment/list/schema.json +135 -0
- package/src/commands/native/customer/segment-customer/list/command.js +171 -0
- package/src/commands/native/customer/segment-customer/list/schema.json +123 -0
- package/src/commands/native/customer/shared/search-input.js +179 -0
- package/src/commands/native/customer/shared/search-page.js +54 -0
- package/src/commands/native/customer/tag/get/command.js +73 -0
- package/src/commands/native/customer/tag/get/schema.json +339 -0
- package/src/commands/native/customer/tagcategory/get/command.js +80 -0
- package/src/commands/native/customer/tagcategory/get/query.js +98 -0
- package/src/commands/native/customer/tagcategory/get/schema.json +339 -0
- package/src/commands/native/customer/tagcategory/list/command.js +127 -0
- package/src/commands/native/customer/tagcategory/list/schema.json +394 -0
- package/src/commands/native/fulfillment/commands.index.json +33 -0
- package/src/commands/native/fulfillment/delivery/get/command.js +107 -0
- package/src/commands/native/fulfillment/delivery/get/projection.js +142 -0
- package/src/commands/native/fulfillment/delivery/get/schema.json +173 -0
- package/src/commands/native/fulfillment/domain.json +3 -0
- package/src/commands/native/fulfillment/shipment/list/command.js +83 -0
- package/src/commands/native/fulfillment/shipment/list/projection.js +130 -0
- package/src/commands/native/fulfillment/shipment/list/schema.json +752 -0
- package/src/commands/native/fulfillment/waybill/get/command.js +193 -0
- package/src/commands/native/fulfillment/waybill/get/response.js +68 -0
- package/src/commands/native/fulfillment/waybill/get/schema.json +190 -0
- package/src/commands/native/interaction/commands.index.json +24 -0
- package/src/commands/native/interaction/domain.json +3 -0
- package/src/commands/native/interaction/lottery/get/command.js +61 -0
- package/src/commands/native/interaction/lottery/get/query.js +152 -0
- package/src/commands/native/interaction/lottery/get/schema.json +1004 -0
- package/src/commands/native/interaction/lottery/list/command.js +176 -0
- package/src/commands/native/interaction/lottery/list/schema.json +898 -0
- package/src/commands/native/item/attribute/list/command.js +122 -0
- package/src/commands/native/item/attribute/list/schema.json +727 -0
- package/src/commands/native/item/brand/list/command.js +114 -0
- package/src/commands/native/item/brand/list/schema.json +725 -0
- package/src/commands/native/item/classification/get/command.js +87 -0
- package/src/commands/native/item/classification/get/schema.json +596 -0
- package/src/commands/native/item/classification/list/command.js +105 -0
- package/src/commands/native/item/classification/list/schema.json +676 -0
- package/src/commands/native/item/commands.index.json +87 -0
- package/src/commands/native/item/domain.json +1 -0
- package/src/commands/native/item/group/list/command.js +122 -0
- package/src/commands/native/item/group/list/schema.json +809 -0
- package/src/commands/native/item/item/get/command.js +122 -0
- package/src/commands/native/item/item/get/schema.json +3799 -0
- package/src/commands/native/item/item/list/command.js +7 -0
- package/src/commands/native/item/item/list/schema.json +1118 -0
- package/src/commands/native/item/item/scan/command.js +7 -0
- package/src/commands/native/item/item/scan/schema.json +1046 -0
- package/src/commands/native/item/item-content/get/command.js +54 -0
- package/src/commands/native/item/item-content/get/schema.json +677 -0
- package/src/commands/native/item/shared/api.js +31 -0
- package/src/commands/native/item/shared/content.js +20 -0
- package/src/commands/native/item/shared/diagnostics.js +61 -0
- package/src/commands/native/item/shared/h5.js +63 -0
- package/src/commands/native/item/shared/identity.js +62 -0
- package/src/commands/native/item/shared/normalize.js +49 -0
- package/src/commands/native/item/shared/scope.js +22 -0
- package/src/commands/native/item/shared/search-command.js +61 -0
- package/src/commands/native/item/shared/search-options.js +150 -0
- package/src/commands/native/item/shared/search.js +158 -0
- package/src/commands/native/member/commands.index.json +60 -0
- package/src/commands/native/member/customer-level/get/command.js +122 -0
- package/src/commands/native/member/customer-level/get/schema.json +300 -0
- package/src/commands/native/member/domain.json +3 -0
- package/src/commands/native/member/growth/get/command.js +64 -0
- package/src/commands/native/member/growth/get/schema.json +283 -0
- package/src/commands/native/member/growth-log/list/command.js +203 -0
- package/src/commands/native/member/growth-log/list/schema.json +695 -0
- package/src/commands/native/member/level/get/command.js +70 -0
- package/src/commands/native/member/level/get/query.js +125 -0
- package/src/commands/native/member/level/get/schema.json +339 -0
- package/src/commands/native/member/level/list/command.js +50 -0
- package/src/commands/native/member/level/list/query.js +109 -0
- package/src/commands/native/member/level/list/schema.json +104 -0
- package/src/commands/native/member/membership/get/command.js +78 -0
- package/src/commands/native/member/membership/get/schema.json +283 -0
- package/src/commands/native/order/commands.index.json +33 -0
- package/src/commands/native/order/domain.json +3 -0
- package/src/commands/native/order/order/get/command.js +155 -0
- package/src/commands/native/order/order/get/schema.json +1167 -0
- package/src/commands/native/order/order/list/command.js +230 -0
- package/src/commands/native/order/order/list/schema.json +1363 -0
- package/src/commands/native/order/order/scan/command.js +148 -0
- package/src/commands/native/order/order/scan/scan.js +89 -0
- package/src/commands/native/order/order/scan/schema.json +1445 -0
- package/src/commands/native/order/shared/projection.js +183 -0
- package/src/commands/native/order/shared/query.js +84 -0
- package/src/commands/native/order/shared/search.js +112 -0
- package/src/commands/native/org/commands.index.json +42 -0
- package/src/commands/native/org/domain.json +3 -0
- package/src/commands/native/org/shared/single-staff.js +115 -0
- package/src/commands/native/org/staff/get/command.js +148 -0
- package/src/commands/native/org/staff/get/schema.json +114 -0
- package/src/commands/native/org/staff-role/get/command.js +45 -0
- package/src/commands/native/org/staff-role/get/schema.json +93 -0
- package/src/commands/native/org/unit/get/command.js +131 -0
- package/src/commands/native/org/unit/get/schema.json +478 -0
- package/src/commands/native/org/unit/list/command.js +187 -0
- package/src/commands/native/org/unit/list/schema.json +828 -0
- package/src/commands/native/points/account/get/command.js +65 -0
- package/src/commands/native/points/account/get/schema.json +303 -0
- package/src/commands/native/points/commands.index.json +33 -0
- package/src/commands/native/points/domain.json +3 -0
- package/src/commands/native/points/log/list/command.js +141 -0
- package/src/commands/native/points/log/list/schema.json +761 -0
- package/src/commands/native/points/rule/list/command.js +82 -0
- package/src/commands/native/points/rule/list/schema.json +294 -0
- package/src/commands/native/privacy/commands.index.json +15 -0
- package/src/commands/native/privacy/domain.json +3 -0
- package/src/commands/native/privacy/field/decrypt/command.js +185 -0
- package/src/commands/native/privacy/field/decrypt/schema.json +656 -0
- package/src/commands/native/privacy/shared/order-source.js +33 -0
- package/src/commands/native/stock/commands.index.json +15 -0
- package/src/commands/native/stock/domain.json +3 -0
- package/src/commands/native/stock/log/list/command.js +207 -0
- package/src/commands/native/stock/log/list/schema.json +922 -0
- package/src/commands/native/valuecard/card/get/command.js +107 -0
- package/src/commands/native/valuecard/card/get/schema.json +683 -0
- package/src/commands/native/valuecard/card/list/command.js +136 -0
- package/src/commands/native/valuecard/card/list/pagination.js +23 -0
- package/src/commands/native/valuecard/card/list/schema.json +780 -0
- package/src/commands/native/valuecard/commands.index.json +24 -0
- package/src/commands/native/valuecard/domain.json +3 -0
- package/src/commands/native/valuecard/shared/projection.js +57 -0
- package/src/commands/system/api.js +47 -0
- package/src/commands/system/auth.js +118 -0
- package/src/commands/system/contracts.js +373 -0
- package/src/commands/system/doctor.js +24 -0
- package/src/commands/system/install.js +41 -0
- package/src/commands/system/printer.js +16 -0
- package/src/commands/system/schema.js +162 -0
- package/src/commands/system/shop-guidance.js +9 -0
- package/src/commands/system/shop.js +237 -0
- package/src/commands/system/skills.js +49 -0
- package/src/execution/beijing-date-time.js +29 -0
- package/src/execution/beijing-time.js +21 -0
- package/src/execution/bounded-input.js +42 -0
- package/src/execution/command-io.js +137 -0
- package/src/execution/command-policy.js +41 -0
- package/src/execution/command-schema.js +131 -0
- package/src/execution/datetime.js +83 -0
- package/src/execution/error-details.js +399 -0
- package/src/execution/error-model.js +82 -0
- package/src/execution/exact-json.js +80 -0
- package/src/execution/list-total.js +10 -0
- package/src/execution/money.js +59 -0
- package/src/execution/parser-error.js +66 -0
- package/src/execution/privacy.js +197 -0
- package/src/execution/public-schema.js +52 -0
- package/src/execution/query-api.js +101 -0
- package/src/execution/range.js +84 -0
- package/src/execution/request-state.js +55 -0
- package/src/execution/schema-completeness.js +116 -0
- package/src/execution/shop-query.js +54 -0
- package/src/execution/target-shop-default.js +23 -0
- package/src/execution/target-shop.js +49 -0
- package/src/identity/context-store.js +54 -0
- package/src/identity/context.js +38 -0
- package/src/installation/install.js +158 -0
- package/src/installation/npm-error.js +93 -0
- package/src/installation/skill.js +113 -0
- package/src/transport/account-api.js +152 -0
- package/src/transport/api.js +199 -0
- 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
|
+
};
|