@zhin.js/plugin-ledger 0.0.0-stage → 1.0.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.
Files changed (72) hide show
  1. package/README.md +83 -2
  2. package/commands/ledger-add/index.js +17 -0
  3. package/commands/ledger-add/index.ts +17 -0
  4. package/commands/ledger-delete/index.js +17 -0
  5. package/commands/ledger-delete/index.ts +17 -0
  6. package/commands/ledger-list/index.js +21 -0
  7. package/commands/ledger-list/index.ts +20 -0
  8. package/commands/ledger-month/index.js +14 -0
  9. package/commands/ledger-month/index.ts +14 -0
  10. package/commands/ledger-report/index.js +23 -0
  11. package/commands/ledger-report/index.ts +25 -0
  12. package/commands/ledger-stats/index.js +21 -0
  13. package/commands/ledger-stats/index.ts +20 -0
  14. package/commands/ledger-today/index.js +14 -0
  15. package/commands/ledger-today/index.ts +14 -0
  16. package/commands/ledger-week/index.js +14 -0
  17. package/commands/ledger-week/index.ts +14 -0
  18. package/lib/config.d.ts +21 -0
  19. package/lib/config.d.ts.map +1 -0
  20. package/lib/config.js +30 -0
  21. package/lib/config.js.map +1 -0
  22. package/lib/entry-input.d.ts +18 -0
  23. package/lib/entry-input.d.ts.map +1 -0
  24. package/lib/entry-input.js +31 -0
  25. package/lib/entry-input.js.map +1 -0
  26. package/lib/identity.d.ts +22 -0
  27. package/lib/identity.d.ts.map +1 -0
  28. package/lib/identity.js +37 -0
  29. package/lib/identity.js.map +1 -0
  30. package/lib/index.d.ts +10 -0
  31. package/lib/index.d.ts.map +1 -0
  32. package/lib/index.js +10 -0
  33. package/lib/index.js.map +1 -0
  34. package/lib/money.d.ts +20 -0
  35. package/lib/money.d.ts.map +1 -0
  36. package/lib/money.js +48 -0
  37. package/lib/money.js.map +1 -0
  38. package/lib/period.d.ts +21 -0
  39. package/lib/period.d.ts.map +1 -0
  40. package/lib/period.js +98 -0
  41. package/lib/period.js.map +1 -0
  42. package/lib/report-format.d.ts +30 -0
  43. package/lib/report-format.d.ts.map +1 -0
  44. package/lib/report-format.js +98 -0
  45. package/lib/report-format.js.map +1 -0
  46. package/lib/runtime.d.ts +3 -0
  47. package/lib/runtime.d.ts.map +1 -0
  48. package/lib/runtime.js +3 -0
  49. package/lib/runtime.js.map +1 -0
  50. package/lib/service.d.ts +33 -0
  51. package/lib/service.d.ts.map +1 -0
  52. package/lib/service.js +206 -0
  53. package/lib/service.js.map +1 -0
  54. package/lib/store.d.ts +48 -0
  55. package/lib/store.d.ts.map +1 -0
  56. package/lib/store.js +162 -0
  57. package/lib/store.js.map +1 -0
  58. package/package.json +90 -3
  59. package/plugin.js +19 -0
  60. package/schedules/daily-report/index.js +10 -0
  61. package/schedules/daily-report/index.ts +10 -0
  62. package/schema.json +31 -0
  63. package/src/config.ts +39 -0
  64. package/src/entry-input.ts +39 -0
  65. package/src/identity.ts +50 -0
  66. package/src/index.ts +29 -0
  67. package/src/money.ts +53 -0
  68. package/src/period.ts +101 -0
  69. package/src/report-format.ts +125 -0
  70. package/src/runtime.ts +4 -0
  71. package/src/service.ts +217 -0
  72. package/src/store.ts +195 -0
package/README.md CHANGED
@@ -1,3 +1,84 @@
1
- # Temporary Holding Version
1
+ # 记账
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `@zhin.js/plugin-ledger` 是个人消费账本:按用户隔离保存每笔消费,按今日、本周、本月汇总并给出分类明细;群聊中不显示金额;可订阅每日私信日报。不依赖 AI。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pnpm add @zhin.js/command @zhin.js/schedule-feature
9
+ zhin install @zhin.js/plugin-ledger
10
+ ```
11
+
12
+ 在项目根 `package.json#zhin` 合并以下挂载(保留已有条目):
13
+
14
+ ```json
15
+ {
16
+ "features": [{ "package": "@zhin.js/schedule-feature" }],
17
+ "plugins": [
18
+ { "package": "@zhin.js/plugin-ledger", "instanceKey": "ledger" }
19
+ ]
20
+ }
21
+ ```
22
+
23
+ 需要持久 Database Host(SQLite、PostgreSQL、MySQL 均可);缺少时插件启动失败,不会退回内存。每日日报需要 Schedule Feature 和支持精确目标投递的 Outbound Host;没有时记账和查询照常可用,只是不能开启日报。
24
+
25
+ ```yaml
26
+ configVersion: 2
27
+ plugins:
28
+ ledger: "@zhin.js/plugin-ledger"
29
+ ledger:
30
+ timezone: Asia/Shanghai
31
+ reportTime: "22:00"
32
+ reportWhenEmpty: false
33
+ ```
34
+
35
+ | 配置 | 默认 | 说明 |
36
+ | --- | --- | --- |
37
+ | `timezone` | `Asia/Shanghai` | 计算今日、本周(周一开始)、本月和日报日期的 IANA 时区;无效时启动失败 |
38
+ | `reportTime` | `22:00` | 日报发送时间(`HH:MM`,按 `timezone`) |
39
+ | `reportWhenEmpty` | `false` | 当天没有记账时是否仍发送日报 |
40
+
41
+ ## 指令
42
+
43
+ 以下示例省略 Endpoint 的命令前缀。每条指令也有英文名(括号内),`--help` 显示参数说明。
44
+
45
+ | 指令 | 说明 |
46
+ | --- | --- |
47
+ | `记 <类别> <金额> [备注]`(`ledger-add`) | 记一笔,例如 `记 午饭 18.5 和同事`。回复带编号 `#1` |
48
+ | `今日消费` / `本周消费` / `本月消费`(`ledger-today` / `ledger-week` / `ledger-month`) | 合计、笔数和按类别的明细与占比 |
49
+ | `分类统计 [今日\|本周\|本月\|全部]`(`ledger-stats`) | 每个类别的合计、笔数、平均、最大单笔和占比,默认本月 |
50
+ | `账单 [今日\|本周\|本月\|全部]`(`ledger-list`) | 最新 20 笔账目及编号,默认本月 |
51
+ | `删账 <编号>`(`ledger-delete`) | 删除自己的一笔账目 |
52
+ | `记账日报 [开启\|关闭\|状态]`(`ledger-report`) | 订阅或取消每日私信日报,默认查看状态 |
53
+
54
+ ### 金额
55
+
56
+ 金额按整数“分”保存,解析时不经过浮点数,`0.1 + 0.2` 合计就是 `¥0.30`。可写 `18`、`18.5`、`18.50`、`¥18.5`、`18.5元` 或全角数字;最多两位小数,必须大于 0,单笔不超过 ¥1000000.00。负数、`1e3`、`1,000` 等写法会被拒绝并提示正确格式。类别最多 20 个字且不能是纯数字,备注最多 100 个字。
57
+
58
+ ### 数据隔离与删除
59
+
60
+ 账目按“适配器 + Bot 账号(Endpoint)+ 发送者”隔离:同一个人在私聊和群里看到的是同一本账,别人看不到也删不了。编号按用户递增,删除后不复用;`删账` 只在自己的账目里查找编号。
61
+
62
+ ### 群聊隐私
63
+
64
+ 群聊和频道里不显示金额:
65
+
66
+ - `记`、`删账` 的回复把金额显示为 `¥***`。
67
+ - 汇总、分类统计和账单优先私信发送完整内容,群里只回复“已私信发送给你”。
68
+ - 平台无法私信时(没有主动投递能力,或投递失败,例如平台不允许给群成员发私信),群里回复隐藏金额的版本,只保留类别和笔数,并提示私聊查看。
69
+
70
+ 是否能私信由运行时能力决定(Outbound Host 是否提供精确目标投递以及投递是否成功),不按平台名单判断。
71
+
72
+ ### 每日日报
73
+
74
+ 发送 `记账日报 开启` 订阅。插件每分钟检查一次,按 `timezone` 到达 `reportTime` 后给订阅者私信当天的合计和分类明细,每人每天最多一次;当天没有记账时默认不发送。在群里开启时会先私信一条确认,私信失败则不开启并提示先私聊 Bot。在 `reportTime` 之后开启的,从第二天开始发送。投递失败只记录日志,当天不重试。
75
+
76
+ ## 开发
77
+
78
+ ```bash
79
+ pnpm --filter @zhin.js/plugin-ledger build
80
+ pnpm --filter @zhin.js/plugin-ledger check
81
+ pnpm --filter @zhin.js/plugin-ledger test
82
+ ```
83
+
84
+ 测试使用 `@zhin.js/testing` 的 `FakePlugin` 与 `FakeClock`,覆盖金额与输入解析、用户隔离、群聊隐私、删除权限、时区下的日/周/月边界和日报调度。
@@ -0,0 +1,17 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { ledgerCaller, replyLedger } from "../../lib/identity.js";
4
+ import { ledgerServiceToken } from "../../lib/runtime.js";
5
+ export default defineCommand({
6
+ description: '记一笔消费:记 <类别> <金额> [备注]',
7
+ alias: ['记'],
8
+ params: {
9
+ entry: { type: 'text', min: 1, max: 200, description: '类别 金额 [备注],例如:午饭 18.5 和同事' },
10
+ },
11
+ async execute({ input, params, use }) {
12
+ return replyLedger(async () => {
13
+ const caller = ledgerCaller(input);
14
+ return use(ledgerServiceToken).addEntry(caller, params.entry);
15
+ });
16
+ },
17
+ });
@@ -0,0 +1,17 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { ledgerCaller, replyLedger } from '../../src/identity.js';
3
+ import { ledgerServiceToken } from '../../src/runtime.js';
4
+
5
+ export default defineCommand({
6
+ description: '记一笔消费:记 <类别> <金额> [备注]',
7
+ alias: ['记'],
8
+ params: {
9
+ entry: { type: 'text', min: 1, max: 200, description: '类别 金额 [备注],例如:午饭 18.5 和同事' },
10
+ },
11
+ async execute({ input, params, use }) {
12
+ return replyLedger(async () => {
13
+ const caller = ledgerCaller(input);
14
+ return use(ledgerServiceToken).addEntry(caller, params.entry);
15
+ });
16
+ },
17
+ });
@@ -0,0 +1,17 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { ledgerCaller, replyLedger } from "../../lib/identity.js";
4
+ import { ledgerServiceToken } from "../../lib/runtime.js";
5
+ export default defineCommand({
6
+ description: '删除自己的一笔记账:删账 <编号>',
7
+ alias: ['删账'],
8
+ params: {
9
+ id: { type: 'integer', min: 1, description: '账目编号(记账回复或「账单」中的 #编号)' },
10
+ },
11
+ async execute({ input, params, use }) {
12
+ return replyLedger(async () => {
13
+ const caller = ledgerCaller(input);
14
+ return use(ledgerServiceToken).deleteEntry(caller, params.id);
15
+ });
16
+ },
17
+ });
@@ -0,0 +1,17 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { ledgerCaller, replyLedger } from '../../src/identity.js';
3
+ import { ledgerServiceToken } from '../../src/runtime.js';
4
+
5
+ export default defineCommand({
6
+ description: '删除自己的一笔记账:删账 <编号>',
7
+ alias: ['删账'],
8
+ params: {
9
+ id: { type: 'integer', min: 1, description: '账目编号(记账回复或「账单」中的 #编号)' },
10
+ },
11
+ async execute({ input, params, use }) {
12
+ return replyLedger(async () => {
13
+ const caller = ledgerCaller(input);
14
+ return use(ledgerServiceToken).deleteEntry(caller, params.id);
15
+ });
16
+ },
17
+ });
@@ -0,0 +1,21 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { ledgerCaller, replyLedger } from "../../lib/identity.js";
4
+ import { parsePeriodWord } from "../../lib/period.js";
5
+ import { ledgerServiceToken } from "../../lib/runtime.js";
6
+ export default defineCommand({
7
+ description: '列出账目及编号(最新 20 笔):账单 [今日|本周|本月|全部],默认本月',
8
+ alias: ['账单'],
9
+ params: {
10
+ period: { type: 'word', optional: true, description: '今日、本周、本月或全部' },
11
+ },
12
+ async execute({ input, params, use }) {
13
+ return replyLedger(async () => {
14
+ const caller = ledgerCaller(input);
15
+ const period = parsePeriodWord(params.period, 'month');
16
+ if (!period)
17
+ return '时间范围只能是:今日、本周、本月、全部';
18
+ return use(ledgerServiceToken).listEntries(caller, period);
19
+ });
20
+ },
21
+ });
@@ -0,0 +1,20 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { ledgerCaller, replyLedger } from '../../src/identity.js';
3
+ import { parsePeriodWord } from '../../src/period.js';
4
+ import { ledgerServiceToken } from '../../src/runtime.js';
5
+
6
+ export default defineCommand({
7
+ description: '列出账目及编号(最新 20 笔):账单 [今日|本周|本月|全部],默认本月',
8
+ alias: ['账单'],
9
+ params: {
10
+ period: { type: 'word', optional: true, description: '今日、本周、本月或全部' },
11
+ },
12
+ async execute({ input, params, use }) {
13
+ return replyLedger(async () => {
14
+ const caller = ledgerCaller(input);
15
+ const period = parsePeriodWord(params.period, 'month');
16
+ if (!period) return '时间范围只能是:今日、本周、本月、全部';
17
+ return use(ledgerServiceToken).listEntries(caller, period);
18
+ });
19
+ },
20
+ });
@@ -0,0 +1,14 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { ledgerCaller, replyLedger } from "../../lib/identity.js";
4
+ import { ledgerServiceToken } from "../../lib/runtime.js";
5
+ export default defineCommand({
6
+ description: '查看本月消费合计与分类明细',
7
+ alias: ['本月消费'],
8
+ async execute({ input, use }) {
9
+ return replyLedger(async () => {
10
+ const caller = ledgerCaller(input);
11
+ return use(ledgerServiceToken).periodSummary(caller, 'month');
12
+ });
13
+ },
14
+ });
@@ -0,0 +1,14 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { ledgerCaller, replyLedger } from '../../src/identity.js';
3
+ import { ledgerServiceToken } from '../../src/runtime.js';
4
+
5
+ export default defineCommand({
6
+ description: '查看本月消费合计与分类明细',
7
+ alias: ['本月消费'],
8
+ async execute({ input, use }) {
9
+ return replyLedger(async () => {
10
+ const caller = ledgerCaller(input);
11
+ return use(ledgerServiceToken).periodSummary(caller, 'month');
12
+ });
13
+ },
14
+ });
@@ -0,0 +1,23 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { ledgerCaller, replyLedger } from "../../lib/identity.js";
4
+ import { ledgerServiceToken } from "../../lib/runtime.js";
5
+ const ACTIONS = {
6
+ 开启: 'on',
7
+ 关闭: 'off',
8
+ 状态: 'status',
9
+ };
10
+ export default defineCommand({
11
+ description: '订阅或取消每日私信记账日报:记账日报 [开启|关闭|状态]',
12
+ alias: ['记账日报'],
13
+ params: {
14
+ action: { type: 'word', optional: true, choices: ['开启', '关闭', '状态'], description: '开启、关闭或状态(默认状态)' },
15
+ },
16
+ async execute({ input, params, use }) {
17
+ return replyLedger(async () => {
18
+ const caller = ledgerCaller(input);
19
+ const action = ACTIONS[params.action ?? '状态'] ?? 'status';
20
+ return use(ledgerServiceToken).dailyReport(caller, action);
21
+ });
22
+ },
23
+ });
@@ -0,0 +1,25 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { ledgerCaller, replyLedger } from '../../src/identity.js';
3
+ import { ledgerServiceToken } from '../../src/runtime.js';
4
+ import type { DailyReportAction } from '../../src/service.js';
5
+
6
+ const ACTIONS: Readonly<Record<string, DailyReportAction>> = {
7
+ 开启: 'on',
8
+ 关闭: 'off',
9
+ 状态: 'status',
10
+ };
11
+
12
+ export default defineCommand({
13
+ description: '订阅或取消每日私信记账日报:记账日报 [开启|关闭|状态]',
14
+ alias: ['记账日报'],
15
+ params: {
16
+ action: { type: 'word', optional: true, choices: ['开启', '关闭', '状态'], description: '开启、关闭或状态(默认状态)' },
17
+ },
18
+ async execute({ input, params, use }) {
19
+ return replyLedger(async () => {
20
+ const caller = ledgerCaller(input);
21
+ const action = ACTIONS[params.action ?? '状态'] ?? 'status';
22
+ return use(ledgerServiceToken).dailyReport(caller, action);
23
+ });
24
+ },
25
+ });
@@ -0,0 +1,21 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { ledgerCaller, replyLedger } from "../../lib/identity.js";
4
+ import { parsePeriodWord } from "../../lib/period.js";
5
+ import { ledgerServiceToken } from "../../lib/runtime.js";
6
+ export default defineCommand({
7
+ description: '按类别统计合计、笔数、平均、最大与占比:分类统计 [今日|本周|本月|全部],默认本月',
8
+ alias: ['分类统计'],
9
+ params: {
10
+ period: { type: 'word', optional: true, description: '今日、本周、本月或全部' },
11
+ },
12
+ async execute({ input, params, use }) {
13
+ return replyLedger(async () => {
14
+ const caller = ledgerCaller(input);
15
+ const period = parsePeriodWord(params.period, 'month');
16
+ if (!period)
17
+ return '时间范围只能是:今日、本周、本月、全部';
18
+ return use(ledgerServiceToken).categoryStatistics(caller, period);
19
+ });
20
+ },
21
+ });
@@ -0,0 +1,20 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { ledgerCaller, replyLedger } from '../../src/identity.js';
3
+ import { parsePeriodWord } from '../../src/period.js';
4
+ import { ledgerServiceToken } from '../../src/runtime.js';
5
+
6
+ export default defineCommand({
7
+ description: '按类别统计合计、笔数、平均、最大与占比:分类统计 [今日|本周|本月|全部],默认本月',
8
+ alias: ['分类统计'],
9
+ params: {
10
+ period: { type: 'word', optional: true, description: '今日、本周、本月或全部' },
11
+ },
12
+ async execute({ input, params, use }) {
13
+ return replyLedger(async () => {
14
+ const caller = ledgerCaller(input);
15
+ const period = parsePeriodWord(params.period, 'month');
16
+ if (!period) return '时间范围只能是:今日、本周、本月、全部';
17
+ return use(ledgerServiceToken).categoryStatistics(caller, period);
18
+ });
19
+ },
20
+ });
@@ -0,0 +1,14 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { ledgerCaller, replyLedger } from "../../lib/identity.js";
4
+ import { ledgerServiceToken } from "../../lib/runtime.js";
5
+ export default defineCommand({
6
+ description: '查看今日消费合计与分类明细',
7
+ alias: ['今日消费'],
8
+ async execute({ input, use }) {
9
+ return replyLedger(async () => {
10
+ const caller = ledgerCaller(input);
11
+ return use(ledgerServiceToken).periodSummary(caller, 'today');
12
+ });
13
+ },
14
+ });
@@ -0,0 +1,14 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { ledgerCaller, replyLedger } from '../../src/identity.js';
3
+ import { ledgerServiceToken } from '../../src/runtime.js';
4
+
5
+ export default defineCommand({
6
+ description: '查看今日消费合计与分类明细',
7
+ alias: ['今日消费'],
8
+ async execute({ input, use }) {
9
+ return replyLedger(async () => {
10
+ const caller = ledgerCaller(input);
11
+ return use(ledgerServiceToken).periodSummary(caller, 'today');
12
+ });
13
+ },
14
+ });
@@ -0,0 +1,14 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { ledgerCaller, replyLedger } from "../../lib/identity.js";
4
+ import { ledgerServiceToken } from "../../lib/runtime.js";
5
+ export default defineCommand({
6
+ description: '查看本周(周一至周日)消费合计与分类明细',
7
+ alias: ['本周消费'],
8
+ async execute({ input, use }) {
9
+ return replyLedger(async () => {
10
+ const caller = ledgerCaller(input);
11
+ return use(ledgerServiceToken).periodSummary(caller, 'week');
12
+ });
13
+ },
14
+ });
@@ -0,0 +1,14 @@
1
+ import { defineCommand } from 'zhin.js/command';
2
+ import { ledgerCaller, replyLedger } from '../../src/identity.js';
3
+ import { ledgerServiceToken } from '../../src/runtime.js';
4
+
5
+ export default defineCommand({
6
+ description: '查看本周(周一至周日)消费合计与分类明细',
7
+ alias: ['本周消费'],
8
+ async execute({ input, use }) {
9
+ return replyLedger(async () => {
10
+ const caller = ledgerCaller(input);
11
+ return use(ledgerServiceToken).periodSummary(caller, 'week');
12
+ });
13
+ },
14
+ });
@@ -0,0 +1,21 @@
1
+ import { Schema } from 'zhin.js';
2
+ export declare const DEFAULT_TIMEZONE = "Asia/Shanghai";
3
+ export declare const DEFAULT_REPORT_TIME = "22:00";
4
+ export declare const ledgerConfig: Schema<Schema.InputRecord<{
5
+ timezone: Schema<string, string> & Schema.Present;
6
+ reportTime: Schema<string, string> & Schema.Present;
7
+ reportWhenEmpty: Schema<boolean, boolean> & Schema.Present;
8
+ }>, {
9
+ timezone: string;
10
+ reportTime: string;
11
+ reportWhenEmpty: boolean;
12
+ } & {}>;
13
+ export type LedgerConfig = Schema.Output<typeof ledgerConfig>;
14
+ export interface ResolvedLedgerConfig {
15
+ readonly timezone: string;
16
+ readonly reportTime: string;
17
+ readonly reportWhenEmpty: boolean;
18
+ }
19
+ /** Applies defaults and fails setup on an invalid timezone or report time instead of guessing. */
20
+ export declare function resolveLedgerConfig(config: Partial<LedgerConfig> | undefined): ResolvedLedgerConfig;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C,eAAO,MAAM,YAAY;;;;;;;;OAIvB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAE9D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAaD,kGAAkG;AAClG,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,GAAG,oBAAoB,CAOnG"}
package/lib/config.js ADDED
@@ -0,0 +1,30 @@
1
+ import { Schema } from 'zhin.js';
2
+ export const DEFAULT_TIMEZONE = 'Asia/Shanghai';
3
+ export const DEFAULT_REPORT_TIME = '22:00';
4
+ export const ledgerConfig = Schema.object({
5
+ timezone: Schema.string().default(DEFAULT_TIMEZONE).description('计算今日、本周(周一开始)、本月与日报日期所用的 IANA 时区').effect('reload'),
6
+ reportTime: Schema.string().default(DEFAULT_REPORT_TIME).description('记账日报的发送时间(HH:MM,按 timezone);只发给用「记账日报 开启」订阅的用户').effect('reload'),
7
+ reportWhenEmpty: Schema.boolean().default(false).description('当天没有记账时是否仍发送日报').effect('reload'),
8
+ });
9
+ const REPORT_TIME_PATTERN = /^(?:[01]\d|2[0-3]):[0-5]\d$/u;
10
+ function assertTimezone(timezone) {
11
+ try {
12
+ if (!timezone || timezone !== timezone.trim() || /^[+-]/u.test(timezone))
13
+ throw new RangeError('Invalid IANA timezone');
14
+ new Intl.DateTimeFormat('zh-CN', { timeZone: timezone }).format(0);
15
+ }
16
+ catch {
17
+ throw new TypeError(`记账 timezone 必须是有效的 IANA 时区:${timezone}`);
18
+ }
19
+ }
20
+ /** Applies defaults and fails setup on an invalid timezone or report time instead of guessing. */
21
+ export function resolveLedgerConfig(config) {
22
+ const timezone = config?.timezone ?? DEFAULT_TIMEZONE;
23
+ const reportTime = config?.reportTime ?? DEFAULT_REPORT_TIME;
24
+ const reportWhenEmpty = config?.reportWhenEmpty ?? false;
25
+ assertTimezone(timezone);
26
+ if (!REPORT_TIME_PATTERN.test(reportTime))
27
+ throw new TypeError(`记账 reportTime 必须是 HH:MM:${reportTime}`);
28
+ return { timezone, reportTime, reportWhenEmpty };
29
+ }
30
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC;AAChD,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAE3C,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,WAAW,CAAC,kCAAkC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACpH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,gDAAgD,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACvI,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;CAChG,CAAC,CAAC;AAUH,MAAM,mBAAmB,GAAG,8BAA8B,CAAC;AAE3D,SAAS,cAAc,CAAC,QAAgB;IACtC,IAAI,CAAC;QACH,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,QAAQ,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,CAAC;QACxH,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,SAAS,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,mBAAmB,CAAC,MAAyC;IAC3E,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,IAAI,gBAAgB,CAAC;IACtD,MAAM,UAAU,GAAG,MAAM,EAAE,UAAU,IAAI,mBAAmB,CAAC;IAC7D,MAAM,eAAe,GAAG,MAAM,EAAE,eAAe,IAAI,KAAK,CAAC;IACzD,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;IACxG,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC;AACnD,CAAC"}
@@ -0,0 +1,18 @@
1
+ export declare const MAX_CATEGORY_LENGTH = 20;
2
+ export declare const MAX_NOTE_LENGTH = 100;
3
+ export declare const ADD_USAGE = "\u683C\u5F0F\uFF1A\u8BB0 <\u7C7B\u522B> <\u91D1\u989D> [\u5907\u6CE8]\uFF0C\u4F8B\u5982\uFF1A\u8BB0 \u5348\u996D 18.5 \u548C\u540C\u4E8B";
4
+ export interface ParsedEntryInput {
5
+ readonly category: string;
6
+ readonly amountCents: number;
7
+ readonly note: string;
8
+ }
9
+ export type EntryInputResult = {
10
+ readonly ok: true;
11
+ readonly entry: ParsedEntryInput;
12
+ } | {
13
+ readonly ok: false;
14
+ readonly reason: string;
15
+ };
16
+ /** Parses the text after `记`: category, amount, then an optional free-form note. */
17
+ export declare function parseEntryInput(text: string): EntryInputResult;
18
+ //# sourceMappingURL=entry-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-input.d.ts","sourceRoot":"","sources":["../src/entry-input.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,eAAO,MAAM,SAAS,6IAAyC,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAMpD,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAgB9D"}
@@ -0,0 +1,31 @@
1
+ import { parseAmountToCents } from './money.js';
2
+ export const MAX_CATEGORY_LENGTH = 20;
3
+ export const MAX_NOTE_LENGTH = 100;
4
+ export const ADD_USAGE = '格式:记 <类别> <金额> [备注],例如:记 午饭 18.5 和同事';
5
+ function rejected(reason) {
6
+ return { ok: false, reason };
7
+ }
8
+ /** Parses the text after `记`: category, amount, then an optional free-form note. */
9
+ export function parseEntryInput(text) {
10
+ const words = text.trim().split(/\s+/u).filter((word) => word !== '');
11
+ if (words.length < 2)
12
+ return rejected(ADD_USAGE);
13
+ const category = words[0];
14
+ const amountText = words[1];
15
+ const note = words.slice(2).join(' ');
16
+ const amount = parseAmountToCents(amountText);
17
+ if (!amount.ok) {
18
+ const categoryLooksLikeAmount = parseAmountToCents(category).ok;
19
+ if (categoryLooksLikeAmount)
20
+ return rejected(`类别要写在金额前面。${ADD_USAGE}`);
21
+ return rejected(`${amount.reason}。${ADD_USAGE}`);
22
+ }
23
+ if (parseAmountToCents(category).ok)
24
+ return rejected(`类别不能是数字。${ADD_USAGE}`);
25
+ if ([...category].length > MAX_CATEGORY_LENGTH)
26
+ return rejected(`类别最多 ${MAX_CATEGORY_LENGTH} 个字`);
27
+ if ([...note].length > MAX_NOTE_LENGTH)
28
+ return rejected(`备注最多 ${MAX_NOTE_LENGTH} 个字`);
29
+ return { ok: true, entry: { category, amountCents: amount.cents, note } };
30
+ }
31
+ //# sourceMappingURL=entry-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-input.js","sourceRoot":"","sources":["../src/entry-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AAEnC,MAAM,CAAC,MAAM,SAAS,GAAG,sCAAsC,CAAC;AAYhE,SAAS,QAAQ,CAAC,MAAc;IAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC/B,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IACtE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,uBAAuB,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAChE,IAAI,uBAAuB;YAAE,OAAO,QAAQ,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;QACvE,OAAO,QAAQ,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,kBAAkB,CAAC,QAAQ,CAAC,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC,WAAW,SAAS,EAAE,CAAC,CAAC;IAC7E,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,MAAM,GAAG,mBAAmB;QAAE,OAAO,QAAQ,CAAC,QAAQ,mBAAmB,KAAK,CAAC,CAAC;IAClG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,eAAe;QAAE,OAAO,QAAQ,CAAC,QAAQ,eAAe,KAAK,CAAC,CAAC;IACtF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type DeliveryTarget } from 'zhin.js';
2
+ import type { CommandMessage } from 'zhin.js/command';
3
+ /** The person using a ledger command and where they are talking from. */
4
+ export interface LedgerCaller {
5
+ /** Stable per-user key: adapter + endpoint (bot account) + sender id. */
6
+ readonly ownerKey: string;
7
+ readonly senderId: string;
8
+ /** The conversation the command came from. */
9
+ readonly conversation: DeliveryTarget;
10
+ /** The caller's own private conversation with the same bot account. */
11
+ readonly privateTarget: DeliveryTarget;
12
+ /** False in groups and channels, where other people can read replies. */
13
+ readonly isPrivate: boolean;
14
+ }
15
+ export declare class LedgerInputError extends Error {
16
+ }
17
+ export declare function ledgerOwnerKey(adapter: string, endpointId: string, senderId: string): string;
18
+ /** Builds the caller from a command message; commands without a real sender are refused. */
19
+ export declare function ledgerCaller(input: CommandMessage | undefined): LedgerCaller;
20
+ /** Turns expected input problems into a reply and lets real failures surface. */
21
+ export declare function replyLedger(operation: () => Promise<string>): Promise<string>;
22
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;IACtC,uEAAuE;IACvE,QAAQ,CAAC,aAAa,EAAE,cAAc,CAAC;IACvC,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,qBAAa,gBAAiB,SAAQ,KAAK;CAAG;AAE9C,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5F;AAED,4FAA4F;AAC5F,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,GAAG,YAAY,CAgB5E;AAED,iFAAiF;AACjF,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAOnF"}
@@ -0,0 +1,37 @@
1
+ import { createDeliveryTarget } from 'zhin.js';
2
+ export class LedgerInputError extends Error {
3
+ }
4
+ export function ledgerOwnerKey(adapter, endpointId, senderId) {
5
+ return JSON.stringify([adapter, endpointId, senderId]);
6
+ }
7
+ /** Builds the caller from a command message; commands without a real sender are refused. */
8
+ export function ledgerCaller(input) {
9
+ const senderId = input?.sender?.id;
10
+ if (!input || !senderId)
11
+ throw new LedgerInputError('记账需要在真实会话中由具体用户发送');
12
+ const conversation = createDeliveryTarget(input);
13
+ const endpoint = conversation.endpoint;
14
+ const isPrivate = conversation.kind === 'private';
15
+ const privateTarget = isPrivate
16
+ ? conversation
17
+ : Object.freeze({ endpoint, kind: 'private', id: senderId });
18
+ return {
19
+ ownerKey: ledgerOwnerKey(endpoint.adapter, endpoint.id, senderId),
20
+ senderId,
21
+ conversation,
22
+ privateTarget,
23
+ isPrivate,
24
+ };
25
+ }
26
+ /** Turns expected input problems into a reply and lets real failures surface. */
27
+ export async function replyLedger(operation) {
28
+ try {
29
+ return await operation();
30
+ }
31
+ catch (error) {
32
+ if (error instanceof LedgerInputError)
33
+ return error.message;
34
+ throw error;
35
+ }
36
+ }
37
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAuB,MAAM,SAAS,CAAC;AAgBpE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;CAAG;AAE9C,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,UAAkB,EAAE,QAAgB;IAClF,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,YAAY,CAAC,KAAiC;IAC5D,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;IACnC,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;IACzE,MAAM,YAAY,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC;IACvC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,KAAK,SAAS,CAAC;IAClD,MAAM,aAAa,GAAmB,SAAS;QAC7C,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC/D,OAAO;QACL,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;QACjE,QAAQ;QACR,YAAY;QACZ,aAAa;QACb,SAAS;KACV,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,SAAgC;IAChE,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,gBAAgB;YAAE,OAAO,KAAK,CAAC,OAAO,CAAC;QAC5D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export { ledgerConfig, resolveLedgerConfig, DEFAULT_REPORT_TIME, DEFAULT_TIMEZONE, type LedgerConfig, type ResolvedLedgerConfig } from './config.js';
2
+ export { parseEntryInput, ADD_USAGE, MAX_CATEGORY_LENGTH, MAX_NOTE_LENGTH, type EntryInputResult, type ParsedEntryInput } from './entry-input.js';
3
+ export { ledgerCaller, ledgerOwnerKey, replyLedger, LedgerInputError, type LedgerCaller } from './identity.js';
4
+ export { formatCents, parseAmountToCents, MASKED_AMOUNT, MAX_ENTRY_CENTS, type AmountParseResult } from './money.js';
5
+ export { describePeriodRange, isInPeriod, localDateKey, localTimeKey, parsePeriodWord, periodLabel, weekEndKey, weekStartKey, type LedgerPeriod, } from './period.js';
6
+ export { formatCategoryStatistics, formatEntryList, formatPeriodSummary, summarize, totalsByCategory, MAX_LISTED_ENTRIES, type CategoryTotal, type LedgerReply, type PeriodSummary, } from './report-format.js';
7
+ export { ledgerServiceToken } from './runtime.js';
8
+ export { LedgerService, type DailyReportAction, type LedgerServiceOptions } from './service.js';
9
+ export { defineLedgerTables, LedgerStore, ENTRY_TABLE, USER_TABLE, PRIVATE_TARGET_TABLE, type LedgerEntry, type LedgerUserRecord } from './store.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACrJ,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClJ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACrH,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export { ledgerConfig, resolveLedgerConfig, DEFAULT_REPORT_TIME, DEFAULT_TIMEZONE } from './config.js';
2
+ export { parseEntryInput, ADD_USAGE, MAX_CATEGORY_LENGTH, MAX_NOTE_LENGTH } from './entry-input.js';
3
+ export { ledgerCaller, ledgerOwnerKey, replyLedger, LedgerInputError } from './identity.js';
4
+ export { formatCents, parseAmountToCents, MASKED_AMOUNT, MAX_ENTRY_CENTS } from './money.js';
5
+ export { describePeriodRange, isInPeriod, localDateKey, localTimeKey, parsePeriodWord, periodLabel, weekEndKey, weekStartKey, } from './period.js';
6
+ export { formatCategoryStatistics, formatEntryList, formatPeriodSummary, summarize, totalsByCategory, MAX_LISTED_ENTRIES, } from './report-format.js';
7
+ export { ledgerServiceToken } from './runtime.js';
8
+ export { LedgerService } from './service.js';
9
+ export { defineLedgerTables, LedgerStore, ENTRY_TABLE, USER_TABLE, PRIVATE_TARGET_TABLE } from './store.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAgD,MAAM,aAAa,CAAC;AACrJ,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAgD,MAAM,kBAAkB,CAAC;AAClJ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAA0B,MAAM,YAAY,CAAC;AACrH,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,GAEb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,GAInB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAqD,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,oBAAoB,EAA2C,MAAM,YAAY,CAAC"}