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,419 @@
|
|
|
1
|
+
import { Console, Effect, Option } from "effect";
|
|
2
|
+
import { Argument, Command, Flag, Prompt } from "effect/unstable/cli";
|
|
3
|
+
import { ApiClient } from "../api/client.js";
|
|
4
|
+
import { CreateExpenseRequest, CreateExpenseResponse, DeleteExpenseRequest, DeleteExpenseResponse, GetExpenseResponse, ListExpensesResponse, UpdateExpenseRequest, UpdateExpenseResponse } from "../api/model.js";
|
|
5
|
+
import { API_WRITE_SCOPE } from "../auth/model.js";
|
|
6
|
+
import { CliInputError } from "../errors.js";
|
|
7
|
+
import { formatTable, printSuccess } from "../output.js";
|
|
8
|
+
import { failInput, parseAllocations, parseMoney, parseOptionalMoney, parsePercentage, localCalendarDate, parseCurrency, parseOptionalCurrency, validateDate, validateDistinctIds, validateIdempotencyKey } from "./expense-input.js";
|
|
9
|
+
import { jsonFlag, reportFailure } from "./shared.js";
|
|
10
|
+
const groupIdFlag = Flag.String("group-id").pipe(Flag.withMetavar("ID"), Flag.withDescription("Only include expenses from this group"), Flag.optional);
|
|
11
|
+
const queryFlag = Flag.String("query").pipe(Flag.withMetavar("TEXT"), Flag.withDescription("Match expense descriptions (maximum 200 characters)"), Flag.optional);
|
|
12
|
+
const fromFlag = Flag.String("from").pipe(Flag.withMetavar("YYYY-MM-DD"), Flag.withDescription("Include expenses on or after this date"), Flag.optional);
|
|
13
|
+
const toFlag = Flag.String("to").pipe(Flag.withMetavar("YYYY-MM-DD"), Flag.withDescription("Include expenses on or before this date"), Flag.optional);
|
|
14
|
+
const paidByIdFlag = Flag.String("paid-by-id").pipe(Flag.withMetavar("MEMBER_ID"), Flag.withDescription("Only include expenses paid by this member"), Flag.optional);
|
|
15
|
+
const isRecurringFlag = Flag.Literals("is-recurring", ["true", "false"]).pipe(Flag.withMetavar("true|false"), Flag.withDescription("Include only recurring-generated or non-recurring expenses"), Flag.map((value) => value === "true"), Flag.optional);
|
|
16
|
+
const isSettledFlag = Flag.Literals("is-settled", ["true", "false"]).pipe(Flag.withMetavar("true|false"), Flag.withDescription("Include only settled or open expenses"), Flag.map((value) => value === "true"), Flag.optional);
|
|
17
|
+
const limitFlag = Flag.Int("limit").pipe(Flag.withMetavar("COUNT"), Flag.withDescription("Return 1 to 50 expenses (default: 20)"), Flag.optional);
|
|
18
|
+
const cursorFlag = Flag.String("cursor").pipe(Flag.withMetavar("CURSOR"), Flag.withDescription("Continue from a previous response's nextCursor"), Flag.optional);
|
|
19
|
+
const createDescriptionFlag = Flag.String("description").pipe(Flag.withMetavar("TEXT"), Flag.withDescription("Expense description (maximum 200 characters)"));
|
|
20
|
+
const createAmountFlag = Flag.String("amount").pipe(Flag.withMetavar("DECIMAL"), Flag.withDescription("Positive amount with no more than two decimal places"));
|
|
21
|
+
const createCurrencyFlag = Flag.String("currency").pipe(Flag.withMetavar("CODE"), Flag.withDescription("Currency: USD, CAD, EUR, or GBP; required to avoid implicit currency"));
|
|
22
|
+
const createPaidByIdFlag = Flag.String("paid-by-id").pipe(Flag.withMetavar("MEMBER_ID"), Flag.withDescription("Member who paid (defaults to the authenticated user)"), Flag.optional);
|
|
23
|
+
const createSplitWithFlag = Flag.String("split-with").pipe(Flag.withMetavar("MEMBER_ID"), Flag.withDescription("Member included in the split; repeat for multiple members (the payer is always included; in a two-person group the other member is used when omitted)"), Flag.between(0, 100));
|
|
24
|
+
const createSplitTypeFlag = Flag.Literals("split-type", ["amount", "percentage", "exact"]).pipe(Flag.withMetavar("amount|percentage|exact"), Flag.withDescription("amount = split equally; percentage; or exact amounts"), Flag.withDefault("amount"));
|
|
25
|
+
const createDateFlag = Flag.String("date").pipe(Flag.withMetavar("YYYY-MM-DD"), Flag.withDescription("Expense date (defaults to today in your local time zone)"), Flag.optional);
|
|
26
|
+
const percentageSplitFlag = Flag.String("percentage-split").pipe(Flag.withMetavar("MEMBER_ID=PERCENT"), Flag.withDescription("Percentage allocation; repeat and ensure the total is 100. Every named member is included in the split"), Flag.between(0, 100));
|
|
27
|
+
const exactSplitFlag = Flag.String("exact-split").pipe(Flag.withMetavar("MEMBER_ID=AMOUNT"), Flag.withDescription("Exact allocation; repeat and ensure the total equals --amount. Every named member is included in the split"), Flag.between(0, 100));
|
|
28
|
+
const idempotencyKeyFlag = Flag.String("idempotency-key").pipe(Flag.withMetavar("KEY"), Flag.withDescription("Reuse a stable key only when retrying the same write"), Flag.optional);
|
|
29
|
+
const updateDescriptionFlag = createDescriptionFlag.pipe(Flag.optional);
|
|
30
|
+
const updateAmountFlag = createAmountFlag.pipe(Flag.optional);
|
|
31
|
+
// Update flags describe changes; create-only defaults do not apply to them.
|
|
32
|
+
const updateCurrencyFlag = Flag.String("currency").pipe(Flag.withMetavar("CODE"), Flag.withDescription("Change the currency: USD, CAD, EUR, or GBP"), Flag.optional);
|
|
33
|
+
const updatePaidByIdFlag = Flag.String("paid-by-id").pipe(Flag.withMetavar("MEMBER_ID"), Flag.withDescription("Change who paid"), Flag.optional);
|
|
34
|
+
const updateSplitWithFlag = Flag.String("split-with").pipe(Flag.withMetavar("MEMBER_ID"), Flag.withDescription("When changing the split, repeat for every member in it besides the payer"), Flag.between(0, 100));
|
|
35
|
+
const updateDateFlag = Flag.String("date").pipe(Flag.withMetavar("YYYY-MM-DD"), Flag.withDescription("Change the expense date (left unchanged when omitted)"), Flag.optional);
|
|
36
|
+
const updateSplitTypeFlag = Flag.Literals("split-type", ["amount", "percentage", "exact"]).pipe(Flag.withMetavar("amount|percentage|exact"), Flag.withDescription("Replace the split mode (amount = split equally) and participant list"), Flag.optional);
|
|
37
|
+
const yesFlag = Flag.Boolean("yes").pipe(Flag.withDescription("Confirm deletion without an interactive prompt"), Flag.withDefault(false));
|
|
38
|
+
const optionalString = (value) => Option.getOrUndefined(value);
|
|
39
|
+
const optionalBoolean = (value) => Option.match(value, {
|
|
40
|
+
onNone: () => undefined,
|
|
41
|
+
onSome: String
|
|
42
|
+
});
|
|
43
|
+
const optionalNumber = (value) => Option.match(value, {
|
|
44
|
+
onNone: () => undefined,
|
|
45
|
+
onSome: String
|
|
46
|
+
});
|
|
47
|
+
const validateOptionalText = (name, value, maximumLength) => Option.match(value, {
|
|
48
|
+
onNone: () => Effect.void,
|
|
49
|
+
onSome: (text) => {
|
|
50
|
+
if (text.length === 0)
|
|
51
|
+
return failInput(`${name} must not be empty`);
|
|
52
|
+
if (maximumLength !== undefined && text.length > maximumLength) {
|
|
53
|
+
return failInput(`${name} must be at most ${maximumLength} characters`);
|
|
54
|
+
}
|
|
55
|
+
return Effect.void;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const expenseQuery = (filters) => Effect.gen(function* () {
|
|
59
|
+
yield* validateOptionalText("group ID", filters.groupId);
|
|
60
|
+
yield* validateOptionalText("query", filters.query, 200);
|
|
61
|
+
yield* validateDate("from", filters.from);
|
|
62
|
+
yield* validateDate("to", filters.to);
|
|
63
|
+
yield* validateOptionalText("payer ID", filters.paidById);
|
|
64
|
+
yield* validateOptionalText("cursor", filters.cursor);
|
|
65
|
+
const from = optionalString(filters.from);
|
|
66
|
+
const to = optionalString(filters.to);
|
|
67
|
+
if (from !== undefined && to !== undefined && from > to) {
|
|
68
|
+
return yield* failInput("from must be on or before to");
|
|
69
|
+
}
|
|
70
|
+
const limit = Option.getOrUndefined(filters.limit);
|
|
71
|
+
if (limit !== undefined && (limit < 1 || limit > 50)) {
|
|
72
|
+
return yield* failInput("limit must be between 1 and 50");
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
groupId: optionalString(filters.groupId),
|
|
76
|
+
query: optionalString(filters.query),
|
|
77
|
+
from,
|
|
78
|
+
to,
|
|
79
|
+
paidById: optionalString(filters.paidById),
|
|
80
|
+
isRecurring: optionalBoolean(filters.isRecurring),
|
|
81
|
+
isSettled: optionalBoolean(filters.isSettled),
|
|
82
|
+
limit: optionalNumber(filters.limit),
|
|
83
|
+
cursor: optionalString(filters.cursor)
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
const expenseStatus = (expense) => expense.isSettled ? "settled" : "open";
|
|
87
|
+
const renderExpensePage = (page) => {
|
|
88
|
+
const content = page.expenses.length === 0
|
|
89
|
+
? "No expenses found."
|
|
90
|
+
: formatTable(["ID", "DATE", "DESCRIPTION", "GROUP", "PAID BY", "AMOUNT", "STATUS"], page.expenses.map((expense) => [
|
|
91
|
+
expense.id,
|
|
92
|
+
expense.date ?? "—",
|
|
93
|
+
expense.description,
|
|
94
|
+
expense.groupName ?? expense.groupId,
|
|
95
|
+
expense.payer.name,
|
|
96
|
+
`${expense.amount} ${expense.currency}`,
|
|
97
|
+
expenseStatus(expense)
|
|
98
|
+
]));
|
|
99
|
+
return page.nextCursor === null
|
|
100
|
+
? content
|
|
101
|
+
: `${content}\n\nNext cursor: ${page.nextCursor}`;
|
|
102
|
+
};
|
|
103
|
+
const renderExpenseDetail = ({ expense }) => {
|
|
104
|
+
const summary = formatTable(["FIELD", "VALUE"], [
|
|
105
|
+
["ID", expense.id],
|
|
106
|
+
["Description", expense.description],
|
|
107
|
+
["Group", expense.groupName ?? expense.groupId],
|
|
108
|
+
["Date", expense.date ?? "—"],
|
|
109
|
+
["Amount", `${expense.amount} ${expense.currency}`],
|
|
110
|
+
["Paid by", expense.payer.name],
|
|
111
|
+
["Status", expenseStatus(expense)],
|
|
112
|
+
["Recurring", expense.isRecurring ? "yes" : "no"]
|
|
113
|
+
]);
|
|
114
|
+
const splits = expense.splits.length === 0
|
|
115
|
+
? "No splits found."
|
|
116
|
+
: formatTable(["MEMBER", "AMOUNT", "PERCENTAGE", "TYPE"], expense.splits.map((split) => [
|
|
117
|
+
split.name,
|
|
118
|
+
split.amount === null ? "—" : `${split.amount} ${expense.currency}`,
|
|
119
|
+
split.percentage === null ? "—" : `${split.percentage}%`,
|
|
120
|
+
split.splitType
|
|
121
|
+
]));
|
|
122
|
+
return `${summary}\n\nSplits\n${splits}`;
|
|
123
|
+
};
|
|
124
|
+
const renderCreatedExpense = ({ expense }) => formatTable(["FIELD", "VALUE"], [
|
|
125
|
+
["ID", expense.id],
|
|
126
|
+
["Description", expense.description],
|
|
127
|
+
["Group", expense.groupId],
|
|
128
|
+
["Date", expense.date],
|
|
129
|
+
["Amount", `${expense.amount} ${expense.currency}`]
|
|
130
|
+
]);
|
|
131
|
+
const renderDeletedExpense = ({ expense }) => `Deleted expense ${expense.id}: ${expense.description} (${expense.amount} ${expense.currency})`;
|
|
132
|
+
const list = Command.make("list", {
|
|
133
|
+
groupId: groupIdFlag,
|
|
134
|
+
query: queryFlag,
|
|
135
|
+
from: fromFlag,
|
|
136
|
+
to: toFlag,
|
|
137
|
+
paidById: paidByIdFlag,
|
|
138
|
+
isRecurring: isRecurringFlag,
|
|
139
|
+
isSettled: isSettledFlag,
|
|
140
|
+
limit: limitFlag,
|
|
141
|
+
cursor: cursorFlag,
|
|
142
|
+
json: jsonFlag
|
|
143
|
+
}, Effect.fn(function* (filters) {
|
|
144
|
+
const api = yield* ApiClient;
|
|
145
|
+
const query = yield* reportFailure(expenseQuery(filters), filters.json);
|
|
146
|
+
const response = yield* reportFailure(api.get({
|
|
147
|
+
path: "/expenses",
|
|
148
|
+
query,
|
|
149
|
+
schema: ListExpensesResponse
|
|
150
|
+
}), filters.json);
|
|
151
|
+
yield* printSuccess({
|
|
152
|
+
data: response,
|
|
153
|
+
json: filters.json,
|
|
154
|
+
renderHuman: renderExpensePage
|
|
155
|
+
});
|
|
156
|
+
})).pipe(Command.withDescription("Search and page through expense history"));
|
|
157
|
+
const get = Command.make("get", {
|
|
158
|
+
expenseId: Argument.String("expense-id"),
|
|
159
|
+
json: jsonFlag
|
|
160
|
+
}, Effect.fn(function* ({ expenseId, json }) {
|
|
161
|
+
const api = yield* ApiClient;
|
|
162
|
+
const response = yield* reportFailure(api.get({
|
|
163
|
+
path: `/expenses/${encodeURIComponent(expenseId)}`,
|
|
164
|
+
schema: GetExpenseResponse
|
|
165
|
+
}), json);
|
|
166
|
+
yield* printSuccess({
|
|
167
|
+
data: response,
|
|
168
|
+
json,
|
|
169
|
+
renderHuman: renderExpenseDetail
|
|
170
|
+
});
|
|
171
|
+
})).pipe(Command.withDescription("Get one expense and its split details"));
|
|
172
|
+
const create = Command.make("create", {
|
|
173
|
+
groupId: Argument.String("group-id"),
|
|
174
|
+
description: createDescriptionFlag,
|
|
175
|
+
amount: createAmountFlag,
|
|
176
|
+
currency: createCurrencyFlag,
|
|
177
|
+
paidById: createPaidByIdFlag,
|
|
178
|
+
splitWith: createSplitWithFlag,
|
|
179
|
+
splitType: createSplitTypeFlag,
|
|
180
|
+
date: createDateFlag,
|
|
181
|
+
percentageSplits: percentageSplitFlag,
|
|
182
|
+
exactSplits: exactSplitFlag,
|
|
183
|
+
idempotencyKey: idempotencyKeyFlag,
|
|
184
|
+
json: jsonFlag
|
|
185
|
+
}, Effect.fn(function* (options) {
|
|
186
|
+
const description = options.description.trim();
|
|
187
|
+
if (description.length === 0 || description.length > 200) {
|
|
188
|
+
return yield* reportFailure(failInput("description must contain 1 to 200 characters"), options.json);
|
|
189
|
+
}
|
|
190
|
+
const amount = yield* reportFailure(parseMoney("amount", options.amount), options.json);
|
|
191
|
+
const currency = yield* reportFailure(parseCurrency(options.currency), options.json);
|
|
192
|
+
yield* reportFailure(validateDate("date", options.date), options.json);
|
|
193
|
+
yield* reportFailure(validateDistinctIds("split-with", options.splitWith), options.json);
|
|
194
|
+
if (options.splitWith.some((id) => id.length === 0)) {
|
|
195
|
+
return yield* reportFailure(failInput("split-with member IDs must not be empty"), options.json);
|
|
196
|
+
}
|
|
197
|
+
const paidById = Option.getOrUndefined(options.paidById);
|
|
198
|
+
if (paidById !== undefined && paidById.length === 0) {
|
|
199
|
+
return yield* reportFailure(failInput("paid-by-id must not be empty"), options.json);
|
|
200
|
+
}
|
|
201
|
+
const suppliedKey = Option.getOrUndefined(options.idempotencyKey);
|
|
202
|
+
yield* reportFailure(validateIdempotencyKey(suppliedKey), options.json);
|
|
203
|
+
const percentageSplits = yield* reportFailure(parseAllocations("percentage-split", options.percentageSplits, parsePercentage), options.json);
|
|
204
|
+
const exactSplits = yield* reportFailure(parseAllocations("exact-split", options.exactSplits, (value) => parseMoney("exact split amount", value)), options.json);
|
|
205
|
+
if (options.splitType === "amount" &&
|
|
206
|
+
(options.percentageSplits.length > 0 || options.exactSplits.length > 0)) {
|
|
207
|
+
return yield* reportFailure(failInput("amount splits do not accept percentage-split or exact-split"), options.json);
|
|
208
|
+
}
|
|
209
|
+
if (options.splitType === "percentage") {
|
|
210
|
+
if (options.percentageSplits.length === 0) {
|
|
211
|
+
return yield* reportFailure(failInput("percentage splits require at least one percentage-split"), options.json);
|
|
212
|
+
}
|
|
213
|
+
if (options.exactSplits.length > 0) {
|
|
214
|
+
return yield* reportFailure(failInput("percentage splits do not accept exact-split"), options.json);
|
|
215
|
+
}
|
|
216
|
+
const total = Object.values(percentageSplits).reduce((sum, value) => sum + value, 0);
|
|
217
|
+
if (Math.abs(total - 100) > 0.01) {
|
|
218
|
+
return yield* reportFailure(failInput("percentage-split values must total 100"), options.json);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (options.splitType === "exact") {
|
|
222
|
+
if (options.exactSplits.length === 0) {
|
|
223
|
+
return yield* reportFailure(failInput("exact splits require at least one exact-split"), options.json);
|
|
224
|
+
}
|
|
225
|
+
if (options.percentageSplits.length > 0) {
|
|
226
|
+
return yield* reportFailure(failInput("exact splits do not accept percentage-split"), options.json);
|
|
227
|
+
}
|
|
228
|
+
const totalInCents = Object.values(exactSplits).reduce((sum, value) => sum + Math.round(value * 100), 0);
|
|
229
|
+
if (totalInCents !== Math.round(amount * 100)) {
|
|
230
|
+
return yield* reportFailure(failInput("exact-split values must total the expense amount"), options.json);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const api = yield* ApiClient;
|
|
234
|
+
const { data: response, replayed } = yield* reportFailure(api.post({
|
|
235
|
+
path: `/groups/${encodeURIComponent(options.groupId)}/expenses`,
|
|
236
|
+
scope: API_WRITE_SCOPE,
|
|
237
|
+
body: {
|
|
238
|
+
description,
|
|
239
|
+
amount,
|
|
240
|
+
currency,
|
|
241
|
+
splitWith: [...options.splitWith],
|
|
242
|
+
splitType: options.splitType,
|
|
243
|
+
...(paidById === undefined ? {} : { paidById }),
|
|
244
|
+
date: Option.getOrElse(options.date, () => localCalendarDate()),
|
|
245
|
+
...(options.splitType === "percentage" ? { percentageSplits } : {}),
|
|
246
|
+
...(options.splitType === "exact" ? { exactSplits } : {})
|
|
247
|
+
},
|
|
248
|
+
bodySchema: CreateExpenseRequest,
|
|
249
|
+
schema: CreateExpenseResponse,
|
|
250
|
+
...(suppliedKey === undefined ? {} : { idempotencyKey: suppliedKey })
|
|
251
|
+
}), options.json);
|
|
252
|
+
yield* printSuccess({
|
|
253
|
+
data: response,
|
|
254
|
+
replayed,
|
|
255
|
+
json: options.json,
|
|
256
|
+
renderHuman: renderCreatedExpense
|
|
257
|
+
});
|
|
258
|
+
})).pipe(Command.withDescription("Create an expense in a group"));
|
|
259
|
+
const update = Command.make("update", {
|
|
260
|
+
expenseId: Argument.String("expense-id"),
|
|
261
|
+
description: updateDescriptionFlag,
|
|
262
|
+
amount: updateAmountFlag,
|
|
263
|
+
currency: updateCurrencyFlag,
|
|
264
|
+
paidById: updatePaidByIdFlag,
|
|
265
|
+
splitWith: updateSplitWithFlag,
|
|
266
|
+
splitType: updateSplitTypeFlag,
|
|
267
|
+
date: updateDateFlag,
|
|
268
|
+
percentageSplits: percentageSplitFlag,
|
|
269
|
+
exactSplits: exactSplitFlag,
|
|
270
|
+
idempotencyKey: idempotencyKeyFlag,
|
|
271
|
+
json: jsonFlag
|
|
272
|
+
}, Effect.fn(function* (options) {
|
|
273
|
+
const rawDescription = Option.getOrUndefined(options.description);
|
|
274
|
+
const description = rawDescription?.trim();
|
|
275
|
+
if (description !== undefined &&
|
|
276
|
+
(description.length === 0 || description.length > 200)) {
|
|
277
|
+
return yield* reportFailure(failInput("description must contain 1 to 200 characters"), options.json);
|
|
278
|
+
}
|
|
279
|
+
const amount = yield* reportFailure(parseOptionalMoney("amount", options.amount), options.json);
|
|
280
|
+
const currency = yield* reportFailure(parseOptionalCurrency(options.currency), options.json);
|
|
281
|
+
yield* reportFailure(validateDate("date", options.date), options.json);
|
|
282
|
+
yield* reportFailure(validateDistinctIds("split-with", options.splitWith), options.json);
|
|
283
|
+
const paidById = Option.getOrUndefined(options.paidById);
|
|
284
|
+
if (paidById !== undefined && paidById.length === 0) {
|
|
285
|
+
return yield* reportFailure(failInput("paid-by-id must not be empty"), options.json);
|
|
286
|
+
}
|
|
287
|
+
const suppliedKey = Option.getOrUndefined(options.idempotencyKey);
|
|
288
|
+
yield* reportFailure(validateIdempotencyKey(suppliedKey), options.json);
|
|
289
|
+
const percentageSplits = yield* reportFailure(parseAllocations("percentage-split", options.percentageSplits, parsePercentage), options.json);
|
|
290
|
+
const exactSplits = yield* reportFailure(parseAllocations("exact-split", options.exactSplits, (value) => parseMoney("exact split amount", value)), options.json);
|
|
291
|
+
const splitType = Option.getOrUndefined(options.splitType);
|
|
292
|
+
const touchesSplit = splitType !== undefined ||
|
|
293
|
+
options.splitWith.length > 0 ||
|
|
294
|
+
options.percentageSplits.length > 0 ||
|
|
295
|
+
options.exactSplits.length > 0;
|
|
296
|
+
if (touchesSplit && splitType === undefined) {
|
|
297
|
+
return yield* reportFailure(failInput("split updates require split-type and the complete participant list"), options.json);
|
|
298
|
+
}
|
|
299
|
+
if (splitType === "amount" &&
|
|
300
|
+
(options.percentageSplits.length > 0 || options.exactSplits.length > 0)) {
|
|
301
|
+
return yield* reportFailure(failInput("amount splits do not accept percentage-split or exact-split"), options.json);
|
|
302
|
+
}
|
|
303
|
+
if (splitType === "percentage") {
|
|
304
|
+
if (options.percentageSplits.length === 0) {
|
|
305
|
+
return yield* reportFailure(failInput("percentage splits require at least one percentage-split"), options.json);
|
|
306
|
+
}
|
|
307
|
+
if (options.exactSplits.length > 0) {
|
|
308
|
+
return yield* reportFailure(failInput("percentage splits do not accept exact-split"), options.json);
|
|
309
|
+
}
|
|
310
|
+
const total = Object.values(percentageSplits).reduce((sum, value) => sum + value, 0);
|
|
311
|
+
if (Math.abs(total - 100) > 0.01) {
|
|
312
|
+
return yield* reportFailure(failInput("percentage-split values must total 100"), options.json);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (splitType === "exact" && options.exactSplits.length === 0) {
|
|
316
|
+
return yield* reportFailure(failInput("exact splits require at least one exact-split"), options.json);
|
|
317
|
+
}
|
|
318
|
+
if (splitType === "exact" && options.percentageSplits.length > 0) {
|
|
319
|
+
return yield* reportFailure(failInput("exact splits do not accept percentage-split"), options.json);
|
|
320
|
+
}
|
|
321
|
+
const date = Option.getOrUndefined(options.date);
|
|
322
|
+
const body = {
|
|
323
|
+
...(description === undefined ? {} : { description }),
|
|
324
|
+
...(amount === undefined ? {} : { amount }),
|
|
325
|
+
...(currency === undefined ? {} : { currency }),
|
|
326
|
+
...(paidById === undefined ? {} : { paidById }),
|
|
327
|
+
...(date === undefined ? {} : { date }),
|
|
328
|
+
...(splitType === undefined
|
|
329
|
+
? {}
|
|
330
|
+
: { splitType, splitWith: [...options.splitWith] }),
|
|
331
|
+
...(splitType === "percentage" ? { percentageSplits } : {}),
|
|
332
|
+
...(splitType === "exact" ? { exactSplits } : {})
|
|
333
|
+
};
|
|
334
|
+
if (Object.keys(body).length === 0) {
|
|
335
|
+
return yield* reportFailure(failInput("provide at least one field to update"), options.json);
|
|
336
|
+
}
|
|
337
|
+
const api = yield* ApiClient;
|
|
338
|
+
if (splitType === "exact") {
|
|
339
|
+
const current = yield* reportFailure(api.get({
|
|
340
|
+
path: `/expenses/${encodeURIComponent(options.expenseId)}`,
|
|
341
|
+
schema: GetExpenseResponse
|
|
342
|
+
}), options.json);
|
|
343
|
+
const expectedInCents = Math.round((amount ?? current.expense.amount) * 100);
|
|
344
|
+
const totalInCents = Object.values(exactSplits).reduce((sum, value) => sum + Math.round(value * 100), 0);
|
|
345
|
+
if (totalInCents !== expectedInCents) {
|
|
346
|
+
return yield* reportFailure(failInput("exact-split values must total the updated expense amount"), options.json);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const { data: response, replayed } = yield* reportFailure(api.patch({
|
|
350
|
+
path: `/expenses/${encodeURIComponent(options.expenseId)}`,
|
|
351
|
+
body,
|
|
352
|
+
bodySchema: UpdateExpenseRequest,
|
|
353
|
+
schema: UpdateExpenseResponse,
|
|
354
|
+
...(suppliedKey === undefined ? {} : { idempotencyKey: suppliedKey })
|
|
355
|
+
}), options.json);
|
|
356
|
+
yield* printSuccess({
|
|
357
|
+
data: response,
|
|
358
|
+
replayed,
|
|
359
|
+
json: options.json,
|
|
360
|
+
renderHuman: renderExpenseDetail
|
|
361
|
+
});
|
|
362
|
+
})).pipe(Command.withDescription("Update an expense"));
|
|
363
|
+
const deleteExpense = Command.make("delete", {
|
|
364
|
+
expenseId: Argument.String("expense-id"),
|
|
365
|
+
yes: yesFlag,
|
|
366
|
+
idempotencyKey: idempotencyKeyFlag,
|
|
367
|
+
json: jsonFlag
|
|
368
|
+
}, Effect.fn(function* ({ expenseId, yes, idempotencyKey, json }) {
|
|
369
|
+
if (!yes && (json || !process.stdin.isTTY)) {
|
|
370
|
+
return yield* reportFailure(failInput("expense deletion requires --yes in non-interactive mode"), json);
|
|
371
|
+
}
|
|
372
|
+
const suppliedKey = Option.getOrUndefined(idempotencyKey);
|
|
373
|
+
yield* reportFailure(validateIdempotencyKey(suppliedKey), json);
|
|
374
|
+
const api = yield* ApiClient;
|
|
375
|
+
const targetResult = yield* Effect.result(api.get({
|
|
376
|
+
path: `/expenses/${encodeURIComponent(expenseId)}`,
|
|
377
|
+
schema: GetExpenseResponse
|
|
378
|
+
}));
|
|
379
|
+
// A retry after a lost response finds the expense already gone. With the
|
|
380
|
+
// original idempotency key, send the delete anyway: the server returns the
|
|
381
|
+
// original result for that key, or a genuine NOT_FOUND.
|
|
382
|
+
const retryingDeletedTarget = targetResult._tag === "Failure" &&
|
|
383
|
+
suppliedKey !== undefined &&
|
|
384
|
+
targetResult.failure._tag === "ApiRequestError" &&
|
|
385
|
+
targetResult.failure.status === 404;
|
|
386
|
+
if (targetResult._tag === "Failure" && !retryingDeletedTarget) {
|
|
387
|
+
return yield* reportFailure(Effect.fail(targetResult.failure), json);
|
|
388
|
+
}
|
|
389
|
+
const target = targetResult._tag === "Success" ? targetResult.success : undefined;
|
|
390
|
+
if (!yes && target !== undefined) {
|
|
391
|
+
yield* Console.log(`About to delete:\n${renderExpenseDetail(target)}`);
|
|
392
|
+
const confirmed = yield* Prompt.run(Prompt.Confirm({
|
|
393
|
+
message: `Permanently delete expense ${target.expense.id}?`,
|
|
394
|
+
initial: false
|
|
395
|
+
}));
|
|
396
|
+
if (!confirmed) {
|
|
397
|
+
yield* Console.log("Deletion cancelled.");
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const { data: response, replayed } = yield* reportFailure(api.delete({
|
|
402
|
+
path: `/expenses/${encodeURIComponent(expenseId)}`,
|
|
403
|
+
body: {
|
|
404
|
+
confirmed: true,
|
|
405
|
+
userIntent: `Delete expense ${expenseId} via AllSquare CLI after explicit confirmation`
|
|
406
|
+
},
|
|
407
|
+
bodySchema: DeleteExpenseRequest,
|
|
408
|
+
schema: DeleteExpenseResponse,
|
|
409
|
+
...(suppliedKey === undefined ? {} : { idempotencyKey: suppliedKey })
|
|
410
|
+
}), json);
|
|
411
|
+
yield* printSuccess({
|
|
412
|
+
data: response,
|
|
413
|
+
replayed,
|
|
414
|
+
json,
|
|
415
|
+
renderHuman: renderDeletedExpense
|
|
416
|
+
});
|
|
417
|
+
})).pipe(Command.withDescription("Delete an expense after explicit confirmation"));
|
|
418
|
+
export const expensesCommand = Command.make("expenses").pipe(Command.withDescription("Inspect and manage expenses"), Command.withSubcommands([list, get, create, update, deleteExpense]));
|
|
419
|
+
//# sourceMappingURL=expenses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expenses.js","sourceRoot":"","sources":["../../src/commands/expenses.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 groupsCommand: Command.Command<"groups", {}, {}, CliInputError | import("../api/client.js").ApiClientError, ApiClient>;
|
|
5
|
+
//# sourceMappingURL=groups.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/commands/groups.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,OAAO,EAAQ,MAAM,qBAAqB,CAAA;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAc5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAoP5C,eAAO,MAAM,aAAa,yGAGzB,CAAA"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
3
|
+
import { ApiClient } from "../api/client.js";
|
|
4
|
+
import { AddGroupMemberRequest, AddGroupMemberResponse, CreateGroupRequest, CreateGroupResponse, ListGroupMembersResponse, ListGroupsResponse } from "../api/model.js";
|
|
5
|
+
import { API_WRITE_SCOPE } from "../auth/model.js";
|
|
6
|
+
import { CliInputError } from "../errors.js";
|
|
7
|
+
import { formatTable, printSuccess } from "../output.js";
|
|
8
|
+
import { parseCurrency } from "./expense-input.js";
|
|
9
|
+
import { jsonFlag, reportFailure } from "./shared.js";
|
|
10
|
+
const nameFlag = Flag.String("name").pipe(Flag.withMetavar("TEXT"), Flag.withDescription("Group name (maximum 100 characters)"));
|
|
11
|
+
const currencyFlag = Flag.String("currency").pipe(Flag.withMetavar("CODE"), Flag.withDescription("Default currency: USD, CAD, EUR, or GBP"));
|
|
12
|
+
const memberEmailFlag = Flag.String("member-email").pipe(Flag.withMetavar("EMAIL"), Flag.withDescription("Initial member to add or invite; repeat for up to 20 addresses"), Flag.between(0, 20));
|
|
13
|
+
const emailFlag = Flag.String("email").pipe(Flag.withMetavar("EMAIL"), Flag.withDescription("Existing account or invitation email address"));
|
|
14
|
+
const idempotencyKeyFlag = Flag.String("idempotency-key").pipe(Flag.withMetavar("KEY"), Flag.withDescription("Reuse a stable key only when retrying the same write"), Flag.optional);
|
|
15
|
+
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
16
|
+
const idempotencyKeyPattern = /^[A-Za-z0-9._:-]+$/;
|
|
17
|
+
const failInput = (message) => Effect.fail(new CliInputError({ message }));
|
|
18
|
+
const normalizeEmail = (email) => {
|
|
19
|
+
const normalized = email.trim().toLowerCase();
|
|
20
|
+
return normalized.length <= 254 && emailPattern.test(normalized)
|
|
21
|
+
? Effect.succeed(normalized)
|
|
22
|
+
: failInput(`invalid email address: ${email}`);
|
|
23
|
+
};
|
|
24
|
+
const validateIdempotencyKey = (value) => value === undefined ||
|
|
25
|
+
(value.length <= 200 && idempotencyKeyPattern.test(value))
|
|
26
|
+
? Effect.void
|
|
27
|
+
: failInput("idempotency-key must contain 1 to 200 letters, numbers, dots, underscores, colons, or hyphens");
|
|
28
|
+
const normalizeMemberEmails = (emails) => Effect.gen(function* () {
|
|
29
|
+
const normalized = [];
|
|
30
|
+
for (const email of emails)
|
|
31
|
+
normalized.push(yield* normalizeEmail(email));
|
|
32
|
+
if (new Set(normalized).size !== normalized.length) {
|
|
33
|
+
return yield* failInput("member-email must not contain duplicate addresses");
|
|
34
|
+
}
|
|
35
|
+
return normalized;
|
|
36
|
+
});
|
|
37
|
+
const renderCreatedGroup = ({ group, membersAdded, membersNotified, invitationsCreated, invitationEmailsSent }) => formatTable(["FIELD", "VALUE"], [
|
|
38
|
+
["ID", group.id],
|
|
39
|
+
["Name", group.name],
|
|
40
|
+
["Default currency", group.defaultCurrency],
|
|
41
|
+
["Members added", String(membersAdded)],
|
|
42
|
+
["Member notifications sent", String(membersNotified)],
|
|
43
|
+
["Invitations created", String(invitationsCreated)],
|
|
44
|
+
["Invitation emails sent", String(invitationEmailsSent)]
|
|
45
|
+
]);
|
|
46
|
+
const renderAddedGroupMember = (response) => formatTable(["FIELD", "VALUE"], [
|
|
47
|
+
["Status", response.status],
|
|
48
|
+
["Group", response.groupId],
|
|
49
|
+
["Email", response.email],
|
|
50
|
+
...(response.status === "invited"
|
|
51
|
+
? [["Invitation expires", response.expiresAt]]
|
|
52
|
+
: [["User ID", response.userId]]),
|
|
53
|
+
["Email sent", response.emailSent ? "yes" : "no"]
|
|
54
|
+
]);
|
|
55
|
+
const list = Command.make("list", { json: jsonFlag }, Effect.fn(function* ({ json }) {
|
|
56
|
+
const api = yield* ApiClient;
|
|
57
|
+
const response = yield* reportFailure(api.get({ path: "/groups", schema: ListGroupsResponse }), json);
|
|
58
|
+
yield* printSuccess({
|
|
59
|
+
data: response,
|
|
60
|
+
json,
|
|
61
|
+
renderHuman: ({ groups }) => groups.length === 0
|
|
62
|
+
? "No groups found."
|
|
63
|
+
: formatTable(["ID", "NAME", "CURRENCY"], groups.map((group) => [group.id, group.name, group.defaultCurrency]))
|
|
64
|
+
});
|
|
65
|
+
})).pipe(Command.withDescription("List groups you can access"));
|
|
66
|
+
const members = Command.make("members", {
|
|
67
|
+
groupId: Argument.String("group-id"),
|
|
68
|
+
json: jsonFlag
|
|
69
|
+
}, Effect.fn(function* ({ groupId, json }) {
|
|
70
|
+
const api = yield* ApiClient;
|
|
71
|
+
const response = yield* reportFailure(api.get({
|
|
72
|
+
path: `/groups/${encodeURIComponent(groupId)}/members`,
|
|
73
|
+
schema: ListGroupMembersResponse
|
|
74
|
+
}), json);
|
|
75
|
+
yield* printSuccess({
|
|
76
|
+
data: response,
|
|
77
|
+
json,
|
|
78
|
+
renderHuman: ({ members }) => members.length === 0
|
|
79
|
+
? "No members found."
|
|
80
|
+
: formatTable(["ID", "NAME", "EMAIL", "CURRENT USER"], members.map((member) => [
|
|
81
|
+
member.id,
|
|
82
|
+
member.name,
|
|
83
|
+
member.email ?? "—",
|
|
84
|
+
member.isCurrentUser ? "yes" : ""
|
|
85
|
+
]))
|
|
86
|
+
});
|
|
87
|
+
})).pipe(Command.withDescription("List members of a group"));
|
|
88
|
+
const create = Command.make("create", {
|
|
89
|
+
name: nameFlag,
|
|
90
|
+
currency: currencyFlag,
|
|
91
|
+
memberEmails: memberEmailFlag,
|
|
92
|
+
idempotencyKey: idempotencyKeyFlag,
|
|
93
|
+
json: jsonFlag
|
|
94
|
+
}, Effect.fn(function* (options) {
|
|
95
|
+
const name = options.name.trim();
|
|
96
|
+
if (name.length === 0 || name.length > 100) {
|
|
97
|
+
return yield* reportFailure(failInput("name must contain 1 to 100 characters"), options.json);
|
|
98
|
+
}
|
|
99
|
+
const currency = yield* reportFailure(parseCurrency(options.currency), options.json);
|
|
100
|
+
const memberEmails = yield* reportFailure(normalizeMemberEmails(options.memberEmails), options.json);
|
|
101
|
+
const suppliedKey = Option.getOrUndefined(options.idempotencyKey);
|
|
102
|
+
yield* reportFailure(validateIdempotencyKey(suppliedKey), options.json);
|
|
103
|
+
const api = yield* ApiClient;
|
|
104
|
+
const { data: response, replayed } = yield* reportFailure(api.post({
|
|
105
|
+
path: "/groups",
|
|
106
|
+
scope: API_WRITE_SCOPE,
|
|
107
|
+
body: {
|
|
108
|
+
name,
|
|
109
|
+
currency,
|
|
110
|
+
...(memberEmails.length === 0 ? {} : { memberEmails: [...memberEmails] })
|
|
111
|
+
},
|
|
112
|
+
bodySchema: CreateGroupRequest,
|
|
113
|
+
schema: CreateGroupResponse,
|
|
114
|
+
...(suppliedKey === undefined ? {} : { idempotencyKey: suppliedKey })
|
|
115
|
+
}), options.json);
|
|
116
|
+
yield* printSuccess({
|
|
117
|
+
data: response,
|
|
118
|
+
replayed,
|
|
119
|
+
json: options.json,
|
|
120
|
+
renderHuman: renderCreatedGroup
|
|
121
|
+
});
|
|
122
|
+
})).pipe(Command.withDescription("Create a group and optionally add or invite members"));
|
|
123
|
+
const addMember = Command.make("add-member", {
|
|
124
|
+
groupId: Argument.String("group-id"),
|
|
125
|
+
email: emailFlag,
|
|
126
|
+
idempotencyKey: idempotencyKeyFlag,
|
|
127
|
+
json: jsonFlag
|
|
128
|
+
}, Effect.fn(function* ({ groupId, email, idempotencyKey, json }) {
|
|
129
|
+
const normalizedEmail = yield* reportFailure(normalizeEmail(email), json);
|
|
130
|
+
const suppliedKey = Option.getOrUndefined(idempotencyKey);
|
|
131
|
+
yield* reportFailure(validateIdempotencyKey(suppliedKey), json);
|
|
132
|
+
const api = yield* ApiClient;
|
|
133
|
+
const { data: response, replayed } = yield* reportFailure(api.post({
|
|
134
|
+
path: `/groups/${encodeURIComponent(groupId)}/members`,
|
|
135
|
+
scope: API_WRITE_SCOPE,
|
|
136
|
+
body: { email: normalizedEmail },
|
|
137
|
+
bodySchema: AddGroupMemberRequest,
|
|
138
|
+
schema: AddGroupMemberResponse,
|
|
139
|
+
...(suppliedKey === undefined ? {} : { idempotencyKey: suppliedKey })
|
|
140
|
+
}), json);
|
|
141
|
+
yield* printSuccess({
|
|
142
|
+
data: response,
|
|
143
|
+
replayed,
|
|
144
|
+
json,
|
|
145
|
+
renderHuman: renderAddedGroupMember
|
|
146
|
+
});
|
|
147
|
+
})).pipe(Command.withDescription("Add an existing user or invite an email address"));
|
|
148
|
+
export const groupsCommand = Command.make("groups").pipe(Command.withDescription("Inspect and manage groups and membership"), Command.withSubcommands([list, members, create, addMember]));
|
|
149
|
+
//# sourceMappingURL=groups.js.map
|