allsquare 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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +234 -0
- package/dist/api/client.d.ts +46 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +162 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/contract.d.ts +549 -0
- package/dist/api/contract.d.ts.map +1 -0
- package/dist/api/contract.js +49 -0
- package/dist/api/contract.js.map +1 -0
- package/dist/api/model.d.ts +627 -0
- package/dist/api/model.d.ts.map +1 -0
- package/dist/api/model.js +378 -0
- package/dist/api/model.js.map +1 -0
- package/dist/auth/auth-service.d.ts +34 -0
- package/dist/auth/auth-service.d.ts.map +1 -0
- package/dist/auth/auth-service.js +221 -0
- package/dist/auth/auth-service.js.map +1 -0
- package/dist/auth/browser.d.ts +12 -0
- package/dist/auth/browser.d.ts.map +1 -0
- package/dist/auth/browser.js +50 -0
- package/dist/auth/browser.js.map +1 -0
- package/dist/auth/credential-store.d.ts +28 -0
- package/dist/auth/credential-store.d.ts.map +1 -0
- package/dist/auth/credential-store.js +75 -0
- package/dist/auth/credential-store.js.map +1 -0
- package/dist/auth/installation-store.d.ts +14 -0
- package/dist/auth/installation-store.d.ts.map +1 -0
- package/dist/auth/installation-store.js +122 -0
- package/dist/auth/installation-store.js.map +1 -0
- package/dist/auth/model.d.ts +54 -0
- package/dist/auth/model.d.ts.map +1 -0
- package/dist/auth/model.js +24 -0
- package/dist/auth/model.js.map +1 -0
- package/dist/auth/oauth-callback.d.ts +17 -0
- package/dist/auth/oauth-callback.d.ts.map +1 -0
- package/dist/auth/oauth-callback.js +94 -0
- package/dist/auth/oauth-callback.js.map +1 -0
- package/dist/auth/oauth-client.d.ts +43 -0
- package/dist/auth/oauth-client.d.ts.map +1 -0
- package/dist/auth/oauth-client.js +191 -0
- package/dist/auth/oauth-client.js.map +1 -0
- package/dist/auth/pkce.d.ts +9 -0
- package/dist/auth/pkce.d.ts.map +1 -0
- package/dist/auth/pkce.js +12 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/command.d.ts +3 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/command.js +24 -0
- package/dist/command.js.map +1 -0
- package/dist/commands/agent-guide.d.ts +5 -0
- package/dist/commands/agent-guide.d.ts.map +1 -0
- package/dist/commands/agent-guide.js +226 -0
- package/dist/commands/agent-guide.js.map +1 -0
- package/dist/commands/auth.d.ts +5 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +68 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/balances.d.ts +5 -0
- package/dist/commands/balances.d.ts.map +1 -0
- package/dist/commands/balances.js +106 -0
- package/dist/commands/balances.js.map +1 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +16 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/expense-input.d.ts +17 -0
- package/dist/commands/expense-input.d.ts.map +1 -0
- package/dist/commands/expense-input.js +78 -0
- package/dist/commands/expense-input.js.map +1 -0
- package/dist/commands/expenses.d.ts +5 -0
- package/dist/commands/expenses.d.ts.map +1 -0
- package/dist/commands/expenses.js +419 -0
- package/dist/commands/expenses.js.map +1 -0
- package/dist/commands/groups.d.ts +5 -0
- package/dist/commands/groups.d.ts.map +1 -0
- package/dist/commands/groups.js +149 -0
- package/dist/commands/groups.js.map +1 -0
- package/dist/commands/recurring-expenses.d.ts +5 -0
- package/dist/commands/recurring-expenses.d.ts.map +1 -0
- package/dist/commands/recurring-expenses.js +416 -0
- package/dist/commands/recurring-expenses.js.map +1 -0
- package/dist/commands/settlements.d.ts +5 -0
- package/dist/commands/settlements.d.ts.map +1 -0
- package/dist/commands/settlements.js +225 -0
- package/dist/commands/settlements.js.map +1 -0
- package/dist/commands/shared.d.ts +6 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +35 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/spend-summary.d.ts +5 -0
- package/dist/commands/spend-summary.d.ts.map +1 -0
- package/dist/commands/spend-summary.js +107 -0
- package/dist/commands/spend-summary.js.map +1 -0
- package/dist/commands/update.d.ts +9 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +78 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +52 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +87 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +183 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction.d.ts +16 -0
- package/dist/interaction.d.ts.map +1 -0
- package/dist/interaction.js +14 -0
- package/dist/interaction.js.map +1 -0
- package/dist/layers.d.ts +13 -0
- package/dist/layers.d.ts.map +1 -0
- package/dist/layers.js +17 -0
- package/dist/layers.js.map +1 -0
- package/dist/output.d.ts +31 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +26 -0
- package/dist/output.js.map +1 -0
- package/dist/update/update-service.d.ts +41 -0
- package/dist/update/update-service.d.ts.map +1 -0
- package/dist/update/update-service.js +231 -0
- package/dist/update/update-service.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Console, Effect } from "effect";
|
|
2
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
3
|
+
import { AuthService } from "../auth/auth-service.js";
|
|
4
|
+
import { CliInputError } from "../errors.js";
|
|
5
|
+
import { printSuccess } from "../output.js";
|
|
6
|
+
import { jsonFlag, reportFailure } from "./shared.js";
|
|
7
|
+
const renderStatus = (status) => {
|
|
8
|
+
if (!status.authenticated) {
|
|
9
|
+
return `Not logged in\nAPI URL: ${status.apiUrl}`;
|
|
10
|
+
}
|
|
11
|
+
return [
|
|
12
|
+
"Logged in",
|
|
13
|
+
`API URL: ${status.apiUrl}`,
|
|
14
|
+
`Scopes: ${status.scopes?.join(", ") ?? "none"}`,
|
|
15
|
+
`Access token expires: ${status.expiresAt ?? "unknown"}`
|
|
16
|
+
].join("\n");
|
|
17
|
+
};
|
|
18
|
+
const printStatus = (status, json) => printSuccess({
|
|
19
|
+
data: status,
|
|
20
|
+
json,
|
|
21
|
+
renderHuman: renderStatus
|
|
22
|
+
});
|
|
23
|
+
const login = Command.make("login", {
|
|
24
|
+
write: Flag.Boolean("write").pipe(Flag.withDescription("Request read and write access (now the default)"), Flag.withDefault(false)),
|
|
25
|
+
readOnly: Flag.Boolean("read-only").pipe(Flag.withDescription("Request read access only"), Flag.withDefault(false)),
|
|
26
|
+
noBrowser: Flag.Boolean("no-browser").pipe(Flag.withDescription("Print the authorization URL without opening a browser"), Flag.withDefault(false)),
|
|
27
|
+
json: jsonFlag
|
|
28
|
+
}, Effect.fn(function* ({ write, readOnly, noBrowser, json }) {
|
|
29
|
+
if (write && readOnly) {
|
|
30
|
+
return yield* reportFailure(Effect.fail(new CliInputError({
|
|
31
|
+
message: "Choose either --read-only or --write, not both"
|
|
32
|
+
})), json);
|
|
33
|
+
}
|
|
34
|
+
const auth = yield* AuthService;
|
|
35
|
+
const status = yield* reportFailure(auth.login({
|
|
36
|
+
write: !readOnly,
|
|
37
|
+
noBrowser,
|
|
38
|
+
onAuthorizationUrl: (url) => Console.error(`Authorize AllSquare CLI:\n${url}`),
|
|
39
|
+
onBrowserOpenError: (error) => Console.error(`Could not open your browser automatically: ${error.message}`)
|
|
40
|
+
}), json);
|
|
41
|
+
if (!json && status.replacedSessionRevocation === "unconfirmed") {
|
|
42
|
+
yield* Console.error("Could not confirm revocation of the previous session; revoke older CLI access in AllSquare settings if needed.");
|
|
43
|
+
}
|
|
44
|
+
yield* printStatus(status, json);
|
|
45
|
+
})).pipe(Command.withDescription("Log in with OAuth authorization code and PKCE"));
|
|
46
|
+
const status = Command.make("status", { json: jsonFlag }, Effect.fn(function* ({ json }) {
|
|
47
|
+
const auth = yield* AuthService;
|
|
48
|
+
const current = yield* reportFailure(auth.status, json);
|
|
49
|
+
yield* printStatus(current, json);
|
|
50
|
+
})).pipe(Command.withDescription("Show the current authentication status"));
|
|
51
|
+
const refresh = Command.make("refresh", { json: jsonFlag }, Effect.fn(function* ({ json }) {
|
|
52
|
+
const auth = yield* AuthService;
|
|
53
|
+
const current = yield* reportFailure(auth.refresh, json);
|
|
54
|
+
yield* printStatus(current, json);
|
|
55
|
+
})).pipe(Command.withDescription("Refresh the current OAuth session"));
|
|
56
|
+
const logout = Command.make("logout", { json: jsonFlag }, Effect.fn(function* ({ json }) {
|
|
57
|
+
const auth = yield* AuthService;
|
|
58
|
+
const current = yield* reportFailure(auth.logout, json);
|
|
59
|
+
yield* printSuccess({
|
|
60
|
+
data: current,
|
|
61
|
+
json,
|
|
62
|
+
renderHuman: () => current.revocation === "unconfirmed"
|
|
63
|
+
? "Logged out locally. Server revocation could not be confirmed; revoke this connection in AllSquare settings if needed."
|
|
64
|
+
: "Logged out"
|
|
65
|
+
});
|
|
66
|
+
})).pipe(Command.withDescription("Revoke the current OAuth session and log out"));
|
|
67
|
+
export const authCommand = Command.make("auth").pipe(Command.withDescription("Manage OAuth authentication"), Command.withSubcommands([login, status, refresh, logout]));
|
|
68
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Command } from "effect/unstable/cli";
|
|
2
|
+
import { ApiClient } from "../api/client.js";
|
|
3
|
+
import { CliInputError } from "../errors.js";
|
|
4
|
+
export declare const balancesCommand: Command.Command<"balances", {}, {}, CliInputError | import("../api/client.js").ApiClientError, ApiClient>;
|
|
5
|
+
//# sourceMappingURL=balances.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"balances.d.ts","sourceRoot":"","sources":["../../src/commands/balances.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,OAAO,EAAQ,MAAM,qBAAqB,CAAA;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAS5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AA2I5C,eAAO,MAAM,eAAe,2GAG3B,CAAA"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
3
|
+
import { ApiClient } from "../api/client.js";
|
|
4
|
+
import { GetAllBalancesResponse, GetGroupBalancesResponse } from "../api/model.js";
|
|
5
|
+
import { CliInputError } from "../errors.js";
|
|
6
|
+
import { formatTable, printSuccess } from "../output.js";
|
|
7
|
+
import { isCurrency } from "./expense-input.js";
|
|
8
|
+
import { jsonFlag, reportFailure } from "./shared.js";
|
|
9
|
+
const currencyFlag = Flag.String("currency").pipe(Flag.withMetavar("CODE"), Flag.withDescription("Convert balances to USD, CAD, EUR, or GBP"), Flag.map((currency) => currency.toUpperCase()), Flag.optional);
|
|
10
|
+
const validateCurrency = (currency) => Option.match(currency, {
|
|
11
|
+
onNone: () => Effect.succeed(Option.none()),
|
|
12
|
+
onSome: (value) => isCurrency(value)
|
|
13
|
+
? Effect.succeed(Option.some(value))
|
|
14
|
+
: Effect.fail(new CliInputError({
|
|
15
|
+
message: "currency must be one of USD, CAD, EUR, GBP"
|
|
16
|
+
}))
|
|
17
|
+
});
|
|
18
|
+
const currencyQuery = (currency) => ({
|
|
19
|
+
currency: Option.getOrUndefined(currency)
|
|
20
|
+
});
|
|
21
|
+
const renderAllBalances = (balances) => {
|
|
22
|
+
const totals = Object.entries(balances.totalsByCurrency);
|
|
23
|
+
const sections = [];
|
|
24
|
+
if (totals.length > 0) {
|
|
25
|
+
sections.push(formatTable(["CURRENCY", "YOU OWE", "OWED TO YOU", "NET"], totals.map(([currency, value]) => [
|
|
26
|
+
currency,
|
|
27
|
+
String(value.youOwe),
|
|
28
|
+
String(value.youAreOwed),
|
|
29
|
+
String(value.net)
|
|
30
|
+
])));
|
|
31
|
+
}
|
|
32
|
+
const people = balances.groups.flatMap((group) => [
|
|
33
|
+
...group.youOwe.map((entry) => [
|
|
34
|
+
group.groupName ?? group.groupId,
|
|
35
|
+
entry.name,
|
|
36
|
+
"you owe",
|
|
37
|
+
String(entry.amount),
|
|
38
|
+
group.currency
|
|
39
|
+
]),
|
|
40
|
+
...group.youAreOwed.map((entry) => [
|
|
41
|
+
group.groupName ?? group.groupId,
|
|
42
|
+
entry.name,
|
|
43
|
+
"owes you",
|
|
44
|
+
String(entry.amount),
|
|
45
|
+
group.currency
|
|
46
|
+
])
|
|
47
|
+
]);
|
|
48
|
+
if (people.length > 0) {
|
|
49
|
+
sections.push(formatTable(["GROUP", "PERSON", "DIRECTION", "AMOUNT", "CURRENCY"], people));
|
|
50
|
+
}
|
|
51
|
+
return sections.length === 0 ? "No open balances." : sections.join("\n\n");
|
|
52
|
+
};
|
|
53
|
+
const renderGroupBalances = (balances) => {
|
|
54
|
+
const rows = [
|
|
55
|
+
...balances.youOwe.map((entry) => [
|
|
56
|
+
entry.name,
|
|
57
|
+
"you owe",
|
|
58
|
+
entry.amount,
|
|
59
|
+
entry.currency
|
|
60
|
+
]),
|
|
61
|
+
...balances.youAreOwed.map((entry) => [
|
|
62
|
+
entry.name,
|
|
63
|
+
"owes you",
|
|
64
|
+
entry.amount,
|
|
65
|
+
entry.currency
|
|
66
|
+
])
|
|
67
|
+
];
|
|
68
|
+
const detail = rows.length === 0
|
|
69
|
+
? "No open balances."
|
|
70
|
+
: formatTable(["PERSON", "DIRECTION", "AMOUNT", "CURRENCY"], rows);
|
|
71
|
+
return `Total spent: ${balances.totalSpent} ${balances.currency}\n\n${detail}`;
|
|
72
|
+
};
|
|
73
|
+
const list = Command.make("list", { currency: currencyFlag, json: jsonFlag }, Effect.fn(function* ({ currency, json }) {
|
|
74
|
+
const api = yield* ApiClient;
|
|
75
|
+
const validatedCurrency = yield* reportFailure(validateCurrency(currency), json);
|
|
76
|
+
const response = yield* reportFailure(api.get({
|
|
77
|
+
path: "/balances",
|
|
78
|
+
query: currencyQuery(validatedCurrency),
|
|
79
|
+
schema: GetAllBalancesResponse
|
|
80
|
+
}), json);
|
|
81
|
+
yield* printSuccess({
|
|
82
|
+
data: response,
|
|
83
|
+
json,
|
|
84
|
+
renderHuman: renderAllBalances
|
|
85
|
+
});
|
|
86
|
+
})).pipe(Command.withDescription("List open balances across all groups"));
|
|
87
|
+
const show = Command.make("show", {
|
|
88
|
+
groupId: Argument.String("group-id"),
|
|
89
|
+
currency: currencyFlag,
|
|
90
|
+
json: jsonFlag
|
|
91
|
+
}, Effect.fn(function* ({ groupId, currency, json }) {
|
|
92
|
+
const api = yield* ApiClient;
|
|
93
|
+
const validatedCurrency = yield* reportFailure(validateCurrency(currency), json);
|
|
94
|
+
const response = yield* reportFailure(api.get({
|
|
95
|
+
path: `/groups/${encodeURIComponent(groupId)}/balances`,
|
|
96
|
+
query: currencyQuery(validatedCurrency),
|
|
97
|
+
schema: GetGroupBalancesResponse
|
|
98
|
+
}), json);
|
|
99
|
+
yield* printSuccess({
|
|
100
|
+
data: response,
|
|
101
|
+
json,
|
|
102
|
+
renderHuman: renderGroupBalances
|
|
103
|
+
});
|
|
104
|
+
})).pipe(Command.withDescription("Show open balances for one group"));
|
|
105
|
+
export const balancesCommand = Command.make("balances").pipe(Command.withDescription("Inspect open balances"), Command.withSubcommands([list, show]));
|
|
106
|
+
//# sourceMappingURL=balances.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"balances.js","sourceRoot":"","sources":["../../src/commands/balances.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAqBxC,eAAO,MAAM,aAAa,qFAGzB,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Command } from "effect/unstable/cli";
|
|
3
|
+
import { CliConfig } from "../config.js";
|
|
4
|
+
import { printSuccess } from "../output.js";
|
|
5
|
+
import { jsonFlag, reportFailure } from "./shared.js";
|
|
6
|
+
const show = Command.make("show", { json: jsonFlag }, Effect.fn(function* ({ json }) {
|
|
7
|
+
const config = yield* CliConfig;
|
|
8
|
+
const apiUrl = yield* reportFailure(config.apiUrl, json);
|
|
9
|
+
yield* printSuccess({
|
|
10
|
+
data: { apiUrl },
|
|
11
|
+
json,
|
|
12
|
+
renderHuman: ({ apiUrl }) => `API URL: ${apiUrl}`
|
|
13
|
+
});
|
|
14
|
+
})).pipe(Command.withDescription("Show the resolved CLI configuration"));
|
|
15
|
+
export const configCommand = Command.make("config").pipe(Command.withDescription("Inspect CLI configuration"), Command.withSubcommands([show]));
|
|
16
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAErD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CACvB,MAAM,EACN,EAAE,IAAI,EAAE,QAAQ,EAAE,EAClB,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAC,EAAE,IAAI,EAAE;IAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,SAAS,CAAA;IAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAExD,KAAK,CAAC,CAAC,YAAY,CAAC;QAClB,IAAI,EAAE,EAAE,MAAM,EAAE;QAChB,IAAI;QACJ,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,YAAY,MAAM,EAAE;KAClD,CAAC,CAAA;AACJ,CAAC,CAAC,CACH,CAAC,IAAI,CACJ,OAAO,CAAC,eAAe,CAAC,qCAAqC,CAAC,CAC/D,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CACtD,OAAO,CAAC,eAAe,CAAC,2BAA2B,CAAC,EACpD,OAAO,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,CAChC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { SUPPORTED_CURRENCIES } from "../api/model.js";
|
|
3
|
+
import { CliInputError } from "../errors.js";
|
|
4
|
+
export declare const failInput: (message: string) => Effect.Effect<never, CliInputError>;
|
|
5
|
+
export declare const localCalendarDate: (now?: Date) => string;
|
|
6
|
+
export type Currency = (typeof SUPPORTED_CURRENCIES)[number];
|
|
7
|
+
export declare const isCurrency: (value: string) => value is Currency;
|
|
8
|
+
export declare const parseCurrency: (raw: string) => Effect.Effect<Currency, CliInputError>;
|
|
9
|
+
export declare const parseOptionalCurrency: (raw: Option.Option<string>) => Effect.Effect<Currency | undefined, CliInputError>;
|
|
10
|
+
export declare const validateDate: (name: string, value: Option.Option<string>) => Effect.Effect<void, CliInputError>;
|
|
11
|
+
export declare const parseMoney: (name: string, value: string) => Effect.Effect<number, CliInputError>;
|
|
12
|
+
export declare const parseOptionalMoney: (name: string, value: Option.Option<string>) => Effect.Effect<number | undefined, CliInputError>;
|
|
13
|
+
export declare const validateIdempotencyKey: (value: string | undefined) => Effect.Effect<void, CliInputError>;
|
|
14
|
+
export declare const parseAllocations: (name: string, values: ReadonlyArray<string>, parseValue: (value: string) => Effect.Effect<number, CliInputError>) => Effect.Effect<Readonly<Record<string, number>>, CliInputError>;
|
|
15
|
+
export declare const parsePercentage: (value: string) => Effect.Effect<number, CliInputError>;
|
|
16
|
+
export declare const validateDistinctIds: (name: string, values: ReadonlyArray<string>) => Effect.Effect<void, CliInputError>;
|
|
17
|
+
//# sourceMappingURL=expense-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expense-input.d.ts","sourceRoot":"","sources":["../../src/commands/expense-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAM5C,eAAO,MAAM,SAAS,GACpB,SAAS,MAAM,KACd,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CACQ,CAAA;AAW7C,eAAO,MAAM,iBAAiB,GAAI,MAAK,IAAiB,KAAG,MAG1D,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAI5D,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,KAAK,IAAI,QACa,CAAA;AAEjE,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,aAAa,CAGhF,CAAA;AAED,eAAO,MAAM,qBAAqB,GAChC,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KACzB,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,SAAS,EAAE,aAAa,CAIhD,CAAA;AAEJ,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAC3B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAMhC,CAAA;AAEJ,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,EACZ,OAAO,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAQrC,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAC3B,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,aAAa,CAI9C,CAAA;AAEJ,eAAO,MAAM,sBAAsB,GACjC,OAAO,MAAM,GAAG,SAAS,KACxB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAM7B,CAAA;AAEP,eAAO,MAAM,gBAAgB,GAC3B,MAAM,MAAM,EACZ,QAAQ,aAAa,CAAC,MAAM,CAAC,EAC7B,YAAY,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,KAClE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,aAAa,CAkB5D,CAAA;AAEJ,eAAO,MAAM,eAAe,GAC1B,OAAO,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAKrC,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,MAAM,MAAM,EACZ,QAAQ,aAAa,CAAC,MAAM,CAAC,KAC5B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAG4B,CAAA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { SUPPORTED_CURRENCIES } from "../api/model.js";
|
|
3
|
+
import { CliInputError } from "../errors.js";
|
|
4
|
+
const datePattern = /^\d{4}-\d{2}-\d{2}$/;
|
|
5
|
+
const moneyPattern = /^(?:0|[1-9]\d*)(?:\.\d{1,2})?$/;
|
|
6
|
+
const idempotencyKeyPattern = /^[A-Za-z0-9._:-]+$/;
|
|
7
|
+
export const failInput = (message) => Effect.fail(new CliInputError({ message }));
|
|
8
|
+
const isIsoCalendarDate = (value) => {
|
|
9
|
+
if (!datePattern.test(value))
|
|
10
|
+
return false;
|
|
11
|
+
const parsed = new Date(`${value}T00:00:00.000Z`);
|
|
12
|
+
return !Number.isNaN(parsed.getTime()) &&
|
|
13
|
+
parsed.toISOString().slice(0, 10) === value;
|
|
14
|
+
};
|
|
15
|
+
// The server cannot know the user's time zone and defaults to the UTC date,
|
|
16
|
+
// which is already tomorrow on a US evening. Send the local calendar date.
|
|
17
|
+
export const localCalendarDate = (now = new Date()) => {
|
|
18
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
19
|
+
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
|
|
20
|
+
};
|
|
21
|
+
const currencyMessage = `currency must be one of ${SUPPORTED_CURRENCIES.join(", ")}`;
|
|
22
|
+
export const isCurrency = (value) => SUPPORTED_CURRENCIES.includes(value);
|
|
23
|
+
export const parseCurrency = (raw) => {
|
|
24
|
+
const currency = raw.trim().toUpperCase();
|
|
25
|
+
return isCurrency(currency) ? Effect.succeed(currency) : failInput(currencyMessage);
|
|
26
|
+
};
|
|
27
|
+
export const parseOptionalCurrency = (raw) => Option.match(raw, {
|
|
28
|
+
onNone: () => Effect.succeed(undefined),
|
|
29
|
+
onSome: parseCurrency
|
|
30
|
+
});
|
|
31
|
+
export const validateDate = (name, value) => Option.match(value, {
|
|
32
|
+
onNone: () => Effect.void,
|
|
33
|
+
onSome: (date) => isIsoCalendarDate(date)
|
|
34
|
+
? Effect.void
|
|
35
|
+
: failInput(`${name} must be a valid YYYY-MM-DD date`)
|
|
36
|
+
});
|
|
37
|
+
export const parseMoney = (name, value) => {
|
|
38
|
+
if (!moneyPattern.test(value)) {
|
|
39
|
+
return failInput(`${name} must be a positive amount with at most two decimal places`);
|
|
40
|
+
}
|
|
41
|
+
const amount = Number(value);
|
|
42
|
+
return amount > 0
|
|
43
|
+
? Effect.succeed(amount)
|
|
44
|
+
: failInput(`${name} must be greater than zero`);
|
|
45
|
+
};
|
|
46
|
+
export const parseOptionalMoney = (name, value) => Option.match(value, {
|
|
47
|
+
onNone: () => Effect.succeed(undefined),
|
|
48
|
+
onSome: (amount) => parseMoney(name, amount)
|
|
49
|
+
});
|
|
50
|
+
export const validateIdempotencyKey = (value) => value === undefined ||
|
|
51
|
+
(value.length <= 200 && idempotencyKeyPattern.test(value))
|
|
52
|
+
? Effect.void
|
|
53
|
+
: failInput("idempotency-key must contain 1 to 200 letters, numbers, dots, underscores, colons, or hyphens");
|
|
54
|
+
export const parseAllocations = (name, values, parseValue) => Effect.gen(function* () {
|
|
55
|
+
const allocations = new Map();
|
|
56
|
+
for (const entry of values) {
|
|
57
|
+
const separator = entry.indexOf("=");
|
|
58
|
+
if (separator <= 0 || separator === entry.length - 1) {
|
|
59
|
+
return yield* failInput(`${name} must use MEMBER_ID=VALUE`);
|
|
60
|
+
}
|
|
61
|
+
const memberId = entry.slice(0, separator);
|
|
62
|
+
if (allocations.has(memberId)) {
|
|
63
|
+
return yield* failInput(`${name} repeats member ID ${memberId}`);
|
|
64
|
+
}
|
|
65
|
+
allocations.set(memberId, yield* parseValue(entry.slice(separator + 1)));
|
|
66
|
+
}
|
|
67
|
+
return Object.fromEntries(allocations);
|
|
68
|
+
});
|
|
69
|
+
export const parsePercentage = (value) => {
|
|
70
|
+
const percentage = Number(value);
|
|
71
|
+
return Number.isFinite(percentage) && percentage >= 0 && percentage <= 100
|
|
72
|
+
? Effect.succeed(percentage)
|
|
73
|
+
: failInput("percentage values must be numbers from 0 through 100");
|
|
74
|
+
};
|
|
75
|
+
export const validateDistinctIds = (name, values) => new Set(values).size === values.length
|
|
76
|
+
? Effect.void
|
|
77
|
+
: failInput(`${name} must not contain duplicate member IDs`);
|
|
78
|
+
//# sourceMappingURL=expense-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expense-input.js","sourceRoot":"","sources":["../../src/commands/expense-input.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Command, Prompt } from "effect/unstable/cli";
|
|
2
|
+
import { ApiClient } from "../api/client.js";
|
|
3
|
+
import { CliInputError } from "../errors.js";
|
|
4
|
+
export declare const expensesCommand: Command.Command<"expenses", {}, {}, CliInputError | import("../api/client.js").ApiClientError | import("effect/Terminal").QuitError, ApiClient | Prompt.Environment>;
|
|
5
|
+
//# sourceMappingURL=expenses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expenses.d.ts","sourceRoot":"","sources":["../../src/commands/expenses.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,OAAO,EAAQ,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAErE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAkB5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAuwB5C,eAAO,MAAM,eAAe,sKAG3B,CAAA"}
|