afdian-kit 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -37,10 +37,13 @@ export default defineConfig({
37
37
  ```ts
38
38
  import {
39
39
  price, listPlans, listProducts, getProduct,
40
- verify, resolveOrder, buildUrl,
40
+ priceSync, listPlansSync, listProductsSync, getProductSync,
41
+ verify, resolveOrder, buildUrl, resolveOrderSync, buildUrlSync,
41
42
  configure, defineConfig,
42
43
  } from "afdian-kit";
43
44
 
45
+ // ── 异步版(Node 场景:首次调用自动发现并加载 afdian.config)──
46
+
44
47
  // 价格(金额单位:分)
45
48
  await price("membership", { months: 6 });
46
49
  // → { kind:"plan", amount:6768, original:7200, rate:0.06, months:6, name:"忍3作战室会员", ... }
@@ -55,6 +58,15 @@ await listPlans({ from: 6, to: 12 }); // 只要 6、12 两档
55
58
  await listProducts(); // [{ key, plan_id, name, price }]
56
59
  await getProduct("starter_pack"); // 未配置 → null
57
60
 
61
+ // ── 同步版 *Sync(打包器/Workers 场景:入口 configure(config) 后即可同步调用)──
62
+ // 调用方只给 key/months —— 月单价、折扣、plan_id 全部由 config 绑定,无需复述
63
+ priceSync("membership", { months: 6 }); // React render 里直接用,无 loading 态
64
+ listPlansSync(); // → 与 listPlans() 同结果
65
+ listProductsSync(); getProductSync("starter_pack");
66
+ buildUrlSync({ entry: "membership", userId, months: 6 });
67
+ resolveOrderSync(order);
68
+ // 未 configure(且从未 await 过异步版)时调用 *Sync → 抛「配置尚未加载」
69
+
58
70
  // 验签(async —— Web Crypto 仅异步 API;不依赖 config)
59
71
  await verify(rawBody); // → { valid:true, type, order } | { valid:false, reason }
60
72
  await verify(rawBody, { publicKey }); // 显式覆盖公钥(轮换/测试)
package/dist/index.d.ts CHANGED
@@ -129,6 +129,13 @@ export declare function defineConfig(config: AfdianConfig): AfdianConfig;
129
129
  export declare function configure(config: AfdianConfig): void;
130
130
  /** 重置缓存(测试用) */
131
131
  export declare function resetConfig(): void;
132
+ /** 获取配置(内部统一入口):已配置直接返回,否则自动发现 */
133
+ export declare function getConfig(): Promise<AfdianConfig>;
134
+ /**
135
+ * 同步获取配置(Sync 系列 API 使用)
136
+ * 需已 configure(config)(打包器/Workers 入口)或此前任一异步方法完成过自动发现(Node)
137
+ */
138
+ export declare function getConfigSync(): AfdianConfig;
132
139
  //#endregion
133
140
  //#region src/pricing.d.ts
134
141
  /** 官方可购档位(一次性购买仅此六档,钉死) */
@@ -136,7 +143,7 @@ export declare const MONTH_TIERS: readonly [1, 3, 6, 12, 24, 36];
136
143
  /** 官方折扣表(钉死) */
137
144
  export declare const DISCOUNTS: Readonly<Record<number, number>>;
138
145
  /**
139
- * 订阅方案价格计算(纯函数)
146
+ * 订阅方案价格计算(纯函数,底层)
140
147
  * 金额 = Math.round(monthly × months × (1 − rate)),单位分
141
148
  */
142
149
  export declare function calcPlanPrice({ monthly, months, discount }: {
@@ -148,7 +155,7 @@ export declare function calcPlanPrice({ monthly, months, discount }: {
148
155
  original: number;
149
156
  rate: number;
150
157
  };
151
- /** 计算某方案全部档位价格 */
158
+ /** 计算某方案全部档位价格(纯函数,底层) */
152
159
  export declare function calcTiers({ monthly, discount }: {
153
160
  monthly: number;
154
161
  discount: boolean;
@@ -158,7 +165,7 @@ export declare function calcTiers({ monthly, discount }: {
158
165
  rate: number;
159
166
  months: 1 | 3 | 6 | 12 | 24 | 36;
160
167
  }[];
161
- /** 订阅方案价格(key 未配置则抛错;product 忽略 months) */
168
+ /** 订阅方案/商品价格(key 未配置则抛错;product 忽略 months) */
162
169
  export declare function price(key: string, opts?: {
163
170
  months?: number;
164
171
  }): Promise<PriceResult>;
@@ -171,6 +178,19 @@ export declare function listPlans(opts?: {
171
178
  export declare function listProducts(): Promise<ProductInfo[]>;
172
179
  /** 单个固定商品(未配置返回 null) */
173
180
  export declare function getProduct(key: string): Promise<ProductInfo | null>;
181
+ /** 同步版 price:需已 configure(config),否则抛错 */
182
+ export declare function priceSync(key: string, opts?: {
183
+ months?: number;
184
+ }): PriceResult;
185
+ /** 同步版 listPlans:需已 configure(config),否则抛错 */
186
+ export declare function listPlansSync(opts?: {
187
+ from?: number;
188
+ to?: number;
189
+ }): PlanInfo[];
190
+ /** 同步版 listProducts:需已 configure(config),否则抛错 */
191
+ export declare function listProductsSync(): ProductInfo[];
192
+ /** 同步版 getProduct:需已 configure(config),否则抛错 */
193
+ export declare function getProductSync(key: string): ProductInfo | null;
174
194
  //#endregion
175
195
  //#region src/verify.d.ts
176
196
  /**
@@ -211,6 +231,14 @@ export declare function buildUrl(opts: {
211
231
  userId: string;
212
232
  months?: number;
213
233
  }): Promise<string>;
234
+ /** 同步版 resolveOrder */
235
+ export declare function resolveOrderSync(order: Pick<AfdianOrder, "plan_id" | "month">): OrderResolution | null;
236
+ /** 同步版 buildUrl */
237
+ export declare function buildUrlSync(opts: {
238
+ entry: string;
239
+ userId: string;
240
+ months?: number;
241
+ }): string;
214
242
  //#endregion
215
243
  //#region src/handler.d.ts
216
244
  /**
package/dist/index.js CHANGED
@@ -49,6 +49,14 @@ async function getConfig() {
49
49
  });
50
50
  return loading;
51
51
  }
52
+ /**
53
+ * 同步获取配置(Sync 系列 API 使用)
54
+ * 需已 configure(config)(打包器/Workers 入口)或此前任一异步方法完成过自动发现(Node)
55
+ */
56
+ function getConfigSync() {
57
+ if (cached) return cached;
58
+ throw new Error("配置尚未加载:请先在入口调用 configure(config);Node 下也可先 await 任一异步方法(如 price/listPlans)触发自动发现");
59
+ }
52
60
  async function discover() {
53
61
  if (!isNode()) throw new Error("自动发现 afdian.config 仅支持 Node 环境;Workers/打包器环境请先调用 configure(config)");
54
62
  const fs = await importNodeBuiltin("fs");
@@ -92,7 +100,7 @@ const DISCOUNTS = {
92
100
  };
93
101
  const TIER_HINT = "1/3/6/12/24/36";
94
102
  /**
95
- * 订阅方案价格计算(纯函数)
103
+ * 订阅方案价格计算(纯函数,底层)
96
104
  * 金额 = Math.round(monthly × months × (1 − rate)),单位分
97
105
  */
98
106
  function calcPlanPrice({ monthly, months, discount }) {
@@ -106,7 +114,7 @@ function calcPlanPrice({ monthly, months, discount }) {
106
114
  rate
107
115
  };
108
116
  }
109
- /** 计算某方案全部档位价格 */
117
+ /** 计算某方案全部档位价格(纯函数,底层) */
110
118
  function calcTiers({ monthly, discount }) {
111
119
  return MONTH_TIERS.map((months) => ({
112
120
  months,
@@ -117,22 +125,21 @@ function calcTiers({ monthly, discount }) {
117
125
  })
118
126
  }));
119
127
  }
120
- /** 订阅方案价格(key 未配置则抛错;product 忽略 months) */
121
- async function price(key, opts) {
122
- const cfg = await getConfig();
128
+ function priceImpl(cfg, key, opts) {
123
129
  const plan = cfg.plans[key];
124
130
  if (plan) {
131
+ const entry = plan;
125
132
  const months = opts?.months ?? 1;
126
133
  const { amount, original, rate } = calcPlanPrice({
127
- monthly: plan.monthly,
134
+ monthly: entry.monthly,
128
135
  months,
129
- discount: plan.discount
136
+ discount: entry.discount
130
137
  });
131
138
  return {
132
139
  key,
133
140
  kind: "plan",
134
- name: plan.name,
135
- plan_id: plan.plan_id,
141
+ name: entry.name,
142
+ plan_id: entry.plan_id,
136
143
  months,
137
144
  amount,
138
145
  original,
@@ -140,19 +147,20 @@ async function price(key, opts) {
140
147
  };
141
148
  }
142
149
  const product = cfg.products[key];
143
- if (product) return {
144
- key,
145
- kind: "product",
146
- name: product.name,
147
- plan_id: product.plan_id,
148
- amount: product.price,
149
- fixed: true
150
- };
150
+ if (product) {
151
+ const entry = product;
152
+ return {
153
+ key,
154
+ kind: "product",
155
+ name: entry.name,
156
+ plan_id: entry.plan_id,
157
+ amount: entry.price,
158
+ fixed: true
159
+ };
160
+ }
151
161
  throw new Error(`未找到配置项: ${key}(检查 afdian.config 的 plans/products)`);
152
162
  }
153
- /** 订阅方案列表(含逐档价格表,可按档位区间过滤) */
154
- async function listPlans(opts) {
155
- const cfg = await getConfig();
163
+ function listPlansImpl(cfg, opts) {
156
164
  return Object.entries(cfg.plans).map(([key, plan]) => ({
157
165
  key,
158
166
  plan_id: plan.plan_id,
@@ -162,9 +170,7 @@ async function listPlans(opts) {
162
170
  tiers: calcTiers(plan).filter((t) => (opts?.from === void 0 || t.months >= opts.from) && (opts?.to === void 0 || t.months <= opts.to))
163
171
  }));
164
172
  }
165
- /** 固定商品列表 */
166
- async function listProducts() {
167
- const cfg = await getConfig();
173
+ function listProductsImpl(cfg) {
168
174
  return Object.entries(cfg.products).map(([key, product]) => ({
169
175
  key,
170
176
  plan_id: product.plan_id,
@@ -172,9 +178,8 @@ async function listProducts() {
172
178
  price: product.price
173
179
  }));
174
180
  }
175
- /** 单个固定商品(未配置返回 null) */
176
- async function getProduct(key) {
177
- const product = (await getConfig()).products[key];
181
+ function getProductImpl(cfg, key) {
182
+ const product = cfg.products[key];
178
183
  if (!product) return null;
179
184
  return {
180
185
  key,
@@ -183,6 +188,38 @@ async function getProduct(key) {
183
188
  price: product.price
184
189
  };
185
190
  }
191
+ /** 订阅方案/商品价格(key 未配置则抛错;product 忽略 months) */
192
+ async function price(key, opts) {
193
+ return priceImpl(await getConfig(), key, opts);
194
+ }
195
+ /** 订阅方案列表(含逐档价格表,可按档位区间过滤) */
196
+ async function listPlans(opts) {
197
+ return listPlansImpl(await getConfig(), opts);
198
+ }
199
+ /** 固定商品列表 */
200
+ async function listProducts() {
201
+ return listProductsImpl(await getConfig());
202
+ }
203
+ /** 单个固定商品(未配置返回 null) */
204
+ async function getProduct(key) {
205
+ return getProductImpl(await getConfig(), key);
206
+ }
207
+ /** 同步版 price:需已 configure(config),否则抛错 */
208
+ function priceSync(key, opts) {
209
+ return priceImpl(getConfigSync(), key, opts);
210
+ }
211
+ /** 同步版 listPlans:需已 configure(config),否则抛错 */
212
+ function listPlansSync(opts) {
213
+ return listPlansImpl(getConfigSync(), opts);
214
+ }
215
+ /** 同步版 listProducts:需已 configure(config),否则抛错 */
216
+ function listProductsSync() {
217
+ return listProductsImpl(getConfigSync());
218
+ }
219
+ /** 同步版 getProduct:需已 configure(config),否则抛错 */
220
+ function getProductSync(key) {
221
+ return getProductImpl(getConfigSync(), key);
222
+ }
186
223
  //#endregion
187
224
  //#region src/verify.ts
188
225
  /**
@@ -273,12 +310,7 @@ async function verify(payload, opts) {
273
310
  }
274
311
  //#endregion
275
312
  //#region src/order.ts
276
- /**
277
- * 按 order.plan_id 匹配配置并给出该单应收金额(应用做金额核对的辅助)
278
- * 匹配不到返回 null(应用记 rejected)
279
- */
280
- async function resolveOrder(order) {
281
- const cfg = await getConfig();
313
+ function resolveOrderImpl(cfg, order) {
282
314
  const planHit = Object.entries(cfg.plans).find(([, entry]) => entry.plan_id === order.plan_id);
283
315
  if (planHit) {
284
316
  const [key, entry] = planHit;
@@ -311,13 +343,7 @@ async function resolveOrder(order) {
311
343
  }
312
344
  return null;
313
345
  }
314
- /**
315
- * 生成爱发电下单跳转 URL
316
- * - plans entry:product_type=0,months 默认 1(非法档位抛错)
317
- * - products entry:product_type=1,不带 month
318
- */
319
- async function buildUrl(opts) {
320
- const cfg = await getConfig();
346
+ function buildUrlImpl(cfg, opts) {
321
347
  const plan = cfg.plans[opts.entry];
322
348
  if (plan) {
323
349
  const months = opts.months ?? 1;
@@ -341,6 +367,29 @@ async function buildUrl(opts) {
341
367
  })}`;
342
368
  throw new Error(`未找到配置项: ${opts.entry}`);
343
369
  }
370
+ /**
371
+ * 按 order.plan_id 匹配配置并给出该单应收金额(应用做金额核对的辅助)
372
+ * 匹配不到返回 null(应用记 rejected)
373
+ */
374
+ async function resolveOrder(order) {
375
+ return resolveOrderImpl(await getConfig(), order);
376
+ }
377
+ /**
378
+ * 生成爱发电下单跳转 URL
379
+ * - plans entry:product_type=0,months 默认 1(非法档位抛错)
380
+ * - products entry:product_type=1,不带 month
381
+ */
382
+ async function buildUrl(opts) {
383
+ return buildUrlImpl(await getConfig(), opts);
384
+ }
385
+ /** 同步版 resolveOrder */
386
+ function resolveOrderSync(order) {
387
+ return resolveOrderImpl(getConfigSync(), order);
388
+ }
389
+ /** 同步版 buildUrl */
390
+ function buildUrlSync(opts) {
391
+ return buildUrlImpl(getConfigSync(), opts);
392
+ }
344
393
  //#endregion
345
394
  //#region src/handler.ts
346
395
  /**
@@ -421,4 +470,4 @@ function createWebhookHandler(deps) {
421
470
  };
422
471
  }
423
472
  //#endregion
424
- export { AFDIAN_PUBLIC_KEY, DISCOUNTS, MONTH_TIERS, buildSignStr, buildUrl, calcPlanPrice, calcTiers, configure, createWebhookHandler, defineConfig, getProduct, listPlans, listProducts, price, resetConfig, resolveOrder, verify };
473
+ export { AFDIAN_PUBLIC_KEY, DISCOUNTS, MONTH_TIERS, buildSignStr, buildUrl, buildUrlSync, calcPlanPrice, calcTiers, configure, createWebhookHandler, defineConfig, getConfig, getConfigSync, getProduct, getProductSync, listPlans, listPlansSync, listProducts, listProductsSync, price, priceSync, resetConfig, resolveOrder, resolveOrderSync, verify };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "afdian-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "爱发电通用工具库:价格算法 / 验签 / 下单跳转 / 配置查询(零依赖 · 无数据库)",
5
5
  "type": "module",
6
6
  "exports": {