@purveyors/cli 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -23
- package/dist/commands/auth.js +4 -4
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/catalog.d.ts +5 -57
- package/dist/commands/catalog.d.ts.map +1 -1
- package/dist/commands/catalog.js +15 -82
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/inventory.d.ts +3 -26
- package/dist/commands/inventory.d.ts.map +1 -1
- package/dist/commands/inventory.js +53 -153
- package/dist/commands/inventory.js.map +1 -1
- package/dist/commands/roast.d.ts +3 -42
- package/dist/commands/roast.d.ts.map +1 -1
- package/dist/commands/roast.js +144 -131
- package/dist/commands/roast.js.map +1 -1
- package/dist/commands/sales.d.ts +3 -11
- package/dist/commands/sales.d.ts.map +1 -1
- package/dist/commands/sales.js +35 -114
- package/dist/commands/sales.js.map +1 -1
- package/dist/commands/tasting.d.ts +5 -39
- package/dist/commands/tasting.d.ts.map +1 -1
- package/dist/commands/tasting.js +21 -111
- package/dist/commands/tasting.js.map +1 -1
- package/dist/index.js +23 -23
- package/dist/index.js.map +1 -1
- package/dist/lib/artisan/db.d.ts +37 -0
- package/dist/lib/artisan/db.d.ts.map +1 -0
- package/dist/lib/artisan/db.js +51 -0
- package/dist/lib/artisan/db.js.map +1 -0
- package/dist/lib/artisan/import.d.ts +16 -0
- package/dist/lib/artisan/import.d.ts.map +1 -0
- package/dist/lib/artisan/import.js +447 -0
- package/dist/lib/artisan/import.js.map +1 -0
- package/dist/lib/artisan/index.d.ts +9 -0
- package/dist/lib/artisan/index.d.ts.map +1 -0
- package/dist/lib/artisan/index.js +7 -0
- package/dist/lib/artisan/index.js.map +1 -0
- package/dist/lib/artisan/parser.d.ts +19 -0
- package/dist/lib/artisan/parser.d.ts.map +1 -0
- package/dist/lib/artisan/parser.js +376 -0
- package/dist/lib/artisan/parser.js.map +1 -0
- package/dist/lib/artisan/temperature.d.ts +52 -0
- package/dist/lib/artisan/temperature.d.ts.map +1 -0
- package/dist/lib/artisan/temperature.js +101 -0
- package/dist/lib/artisan/temperature.js.map +1 -0
- package/dist/lib/artisan/types.d.ts +195 -0
- package/dist/lib/artisan/types.d.ts.map +1 -0
- package/dist/lib/artisan/types.js +35 -0
- package/dist/lib/artisan/types.js.map +1 -0
- package/dist/lib/artisan/validator.d.ts +14 -0
- package/dist/lib/artisan/validator.d.ts.map +1 -0
- package/dist/lib/artisan/validator.js +228 -0
- package/dist/lib/artisan/validator.js.map +1 -0
- package/dist/lib/catalog.d.ts +87 -0
- package/dist/lib/catalog.d.ts.map +1 -0
- package/dist/lib/catalog.js +111 -0
- package/dist/lib/catalog.js.map +1 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +20 -2
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.js +2 -2
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +11 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/inventory.d.ts +80 -0
- package/dist/lib/inventory.d.ts.map +1 -0
- package/dist/lib/inventory.js +205 -0
- package/dist/lib/inventory.js.map +1 -0
- package/dist/lib/roast.d.ts +127 -0
- package/dist/lib/roast.d.ts.map +1 -0
- package/dist/lib/roast.js +284 -0
- package/dist/lib/roast.js.map +1 -0
- package/dist/lib/sales.d.ts +53 -0
- package/dist/lib/sales.d.ts.map +1 -0
- package/dist/lib/sales.js +155 -0
- package/dist/lib/sales.js.map +1 -0
- package/dist/lib/supabase.js +2 -2
- package/dist/lib/supabase.js.map +1 -1
- package/dist/lib/tasting.d.ts +76 -0
- package/dist/lib/tasting.d.ts.map +1 -0
- package/dist/lib/tasting.js +136 -0
- package/dist/lib/tasting.js.map +1 -0
- package/package.json +24 -13
package/dist/commands/sales.js
CHANGED
|
@@ -3,11 +3,10 @@ import { createAuthenticatedClient } from '../lib/supabase.js';
|
|
|
3
3
|
import { outputData, info, success } from '../lib/output.js';
|
|
4
4
|
import { withErrorHandling, AuthError, PrvrsError } from '../lib/errors.js';
|
|
5
5
|
import { confirm, todayIso } from '../lib/prompts.js';
|
|
6
|
-
|
|
7
|
-
const SALE_SELECT = 'id, roast_id, oz_sold, sale_price, buyer, sell_date, user, last_updated';
|
|
6
|
+
import { listSales, recordSale, updateSale, deleteSale } from '../lib/sales.js';
|
|
8
7
|
// ─── Command builder ──────────────────────────────────────────────────────────
|
|
9
8
|
/**
|
|
10
|
-
* `
|
|
9
|
+
* `purvey sales` — Record and manage coffee sales.
|
|
11
10
|
* Requires authentication.
|
|
12
11
|
*/
|
|
13
12
|
export function buildSalesCommand() {
|
|
@@ -22,19 +21,12 @@ export function buildSalesCommand() {
|
|
|
22
21
|
const supabase = await createAuthenticatedClient();
|
|
23
22
|
const { data: { user }, } = await supabase.auth.getUser();
|
|
24
23
|
if (!user) {
|
|
25
|
-
throw new AuthError('Not logged in. Run `
|
|
24
|
+
throw new AuthError('Not logged in. Run `purvey auth login` first.');
|
|
26
25
|
}
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
.select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
|
|
32
|
-
.eq('user', user.id)
|
|
33
|
-
.order('sell_date', { ascending: false })
|
|
34
|
-
.limit(limit);
|
|
35
|
-
if (error)
|
|
36
|
-
throw error;
|
|
37
|
-
if (!data || data.length === 0) {
|
|
26
|
+
const data = await listSales(supabase, user.id, {
|
|
27
|
+
limit: Math.max(1, parseInt(opts.limit, 10)),
|
|
28
|
+
});
|
|
29
|
+
if (data.length === 0) {
|
|
38
30
|
info('No sales found.');
|
|
39
31
|
return;
|
|
40
32
|
}
|
|
@@ -54,7 +46,7 @@ export function buildSalesCommand() {
|
|
|
54
46
|
const supabase = await createAuthenticatedClient();
|
|
55
47
|
const { data: { user }, } = await supabase.auth.getUser();
|
|
56
48
|
if (!user) {
|
|
57
|
-
throw new AuthError('Not logged in. Run `
|
|
49
|
+
throw new AuthError('Not logged in. Run `purvey auth login` first.');
|
|
58
50
|
}
|
|
59
51
|
const roastId = parseInt(opts.roastId, 10);
|
|
60
52
|
if (isNaN(roastId)) {
|
|
@@ -68,42 +60,14 @@ export function buildSalesCommand() {
|
|
|
68
60
|
if (isNaN(price) || price < 0) {
|
|
69
61
|
throw new PrvrsError('INVALID_ARGUMENT', `Invalid --price: "${opts.price}". Must be a non-negative number.`);
|
|
70
62
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
throw new AuthError(`Roast profile ${roastId} not found or does not belong to you.`);
|
|
80
|
-
}
|
|
81
|
-
const insertPayload = {
|
|
82
|
-
user: user.id,
|
|
83
|
-
roast_id: roastId,
|
|
84
|
-
oz_sold: oz,
|
|
85
|
-
sale_price: price,
|
|
86
|
-
sell_date: opts.sellDate ?? todayIso(),
|
|
87
|
-
};
|
|
88
|
-
if (opts.buyer !== undefined) {
|
|
89
|
-
insertPayload.buyer = opts.buyer;
|
|
90
|
-
}
|
|
91
|
-
const { data: inserted, error: insertError } = await supabase
|
|
92
|
-
.from('sales')
|
|
93
|
-
.insert(insertPayload)
|
|
94
|
-
.select('id')
|
|
95
|
-
.single();
|
|
96
|
-
if (insertError)
|
|
97
|
-
throw insertError;
|
|
98
|
-
// Re-fetch the full row with roast join
|
|
99
|
-
const { data, error } = await supabase
|
|
100
|
-
.from('sales')
|
|
101
|
-
.select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
|
|
102
|
-
.eq('id', inserted.id)
|
|
103
|
-
.single();
|
|
104
|
-
if (error)
|
|
105
|
-
throw error;
|
|
106
|
-
success(`Sale ${inserted.id} recorded.`);
|
|
63
|
+
const data = await recordSale(supabase, user.id, {
|
|
64
|
+
roastId,
|
|
65
|
+
oz,
|
|
66
|
+
price,
|
|
67
|
+
buyer: opts.buyer,
|
|
68
|
+
sellDate: opts.sellDate ?? todayIso(),
|
|
69
|
+
});
|
|
70
|
+
success(`Sale ${data.id} recorded.`);
|
|
107
71
|
outputData(data, globalOpts);
|
|
108
72
|
}));
|
|
109
73
|
// ── sales update <id> ─────────────────────────────────────────────────────
|
|
@@ -119,60 +83,36 @@ export function buildSalesCommand() {
|
|
|
119
83
|
const supabase = await createAuthenticatedClient();
|
|
120
84
|
const { data: { user }, } = await supabase.auth.getUser();
|
|
121
85
|
if (!user) {
|
|
122
|
-
throw new AuthError('Not logged in. Run `
|
|
86
|
+
throw new AuthError('Not logged in. Run `purvey auth login` first.');
|
|
123
87
|
}
|
|
124
88
|
const saleId = parseInt(id, 10);
|
|
125
89
|
if (isNaN(saleId)) {
|
|
126
90
|
throw new PrvrsError('INVALID_ARGUMENT', `Invalid sale ID: "${id}".`);
|
|
127
91
|
}
|
|
128
|
-
|
|
129
|
-
const { data: existing, error: fetchError } = await supabase
|
|
130
|
-
.from('sales')
|
|
131
|
-
.select('id')
|
|
132
|
-
.eq('id', saleId)
|
|
133
|
-
.eq('user', user.id)
|
|
134
|
-
.single();
|
|
135
|
-
if (fetchError || !existing) {
|
|
136
|
-
throw new AuthError(`Sale ${id} not found or does not belong to you.`);
|
|
137
|
-
}
|
|
138
|
-
// Build partial update
|
|
139
|
-
const updates = {};
|
|
92
|
+
let oz;
|
|
140
93
|
if (opts.oz !== undefined) {
|
|
141
|
-
|
|
94
|
+
oz = parseFloat(opts.oz);
|
|
142
95
|
if (isNaN(oz) || oz <= 0)
|
|
143
96
|
throw new PrvrsError('INVALID_ARGUMENT', `Invalid --oz: "${opts.oz}".`);
|
|
144
|
-
updates.oz_sold = oz;
|
|
145
97
|
}
|
|
98
|
+
let price;
|
|
146
99
|
if (opts.price !== undefined) {
|
|
147
|
-
|
|
100
|
+
price = parseFloat(opts.price);
|
|
148
101
|
if (isNaN(price) || price < 0)
|
|
149
102
|
throw new PrvrsError('INVALID_ARGUMENT', `Invalid --price: "${opts.price}".`);
|
|
150
|
-
updates.sale_price = price;
|
|
151
103
|
}
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
updates.sell_date = opts.sellDate;
|
|
157
|
-
}
|
|
158
|
-
if (Object.keys(updates).length === 0) {
|
|
104
|
+
if (oz === undefined &&
|
|
105
|
+
price === undefined &&
|
|
106
|
+
opts.buyer === undefined &&
|
|
107
|
+
opts.sellDate === undefined) {
|
|
159
108
|
throw new PrvrsError('INVALID_ARGUMENT', 'No update fields provided. Pass at least one of: --oz, --price, --buyer, --sell-date.');
|
|
160
109
|
}
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
.
|
|
165
|
-
.
|
|
166
|
-
|
|
167
|
-
throw updateError;
|
|
168
|
-
// Re-fetch the updated row
|
|
169
|
-
const { data, error } = await supabase
|
|
170
|
-
.from('sales')
|
|
171
|
-
.select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
|
|
172
|
-
.eq('id', saleId)
|
|
173
|
-
.single();
|
|
174
|
-
if (error)
|
|
175
|
-
throw error;
|
|
110
|
+
const data = await updateSale(supabase, user.id, saleId, {
|
|
111
|
+
oz,
|
|
112
|
+
price,
|
|
113
|
+
buyer: opts.buyer,
|
|
114
|
+
sellDate: opts.sellDate,
|
|
115
|
+
});
|
|
176
116
|
success(`Sale ${saleId} updated.`);
|
|
177
117
|
outputData(data, globalOpts);
|
|
178
118
|
}));
|
|
@@ -182,43 +122,24 @@ export function buildSalesCommand() {
|
|
|
182
122
|
.description('Delete a sale (must be yours)')
|
|
183
123
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
184
124
|
.action(withErrorHandling(async (id, opts, cmd) => {
|
|
185
|
-
void cmd;
|
|
125
|
+
void cmd;
|
|
186
126
|
const supabase = await createAuthenticatedClient();
|
|
187
127
|
const { data: { user }, } = await supabase.auth.getUser();
|
|
188
128
|
if (!user) {
|
|
189
|
-
throw new AuthError('Not logged in. Run `
|
|
129
|
+
throw new AuthError('Not logged in. Run `purvey auth login` first.');
|
|
190
130
|
}
|
|
191
131
|
const saleId = parseInt(id, 10);
|
|
192
132
|
if (isNaN(saleId)) {
|
|
193
133
|
throw new PrvrsError('INVALID_ARGUMENT', `Invalid sale ID: "${id}".`);
|
|
194
134
|
}
|
|
195
|
-
// Verify ownership
|
|
196
|
-
const { data: existing, error: fetchError } = await supabase
|
|
197
|
-
.from('sales')
|
|
198
|
-
.select('id, sell_date, oz_sold')
|
|
199
|
-
.eq('id', saleId)
|
|
200
|
-
.eq('user', user.id)
|
|
201
|
-
.single();
|
|
202
|
-
if (fetchError || !existing) {
|
|
203
|
-
throw new AuthError(`Sale ${id} not found or does not belong to you.`);
|
|
204
|
-
}
|
|
205
135
|
if (!opts.yes) {
|
|
206
|
-
const
|
|
207
|
-
? `from ${existing.sell_date} (${existing.oz_sold} oz)`
|
|
208
|
-
: `#${saleId}`;
|
|
209
|
-
const ok = await confirm(`Delete sale ${label}?`);
|
|
136
|
+
const ok = await confirm(`Delete sale #${saleId}?`);
|
|
210
137
|
if (!ok) {
|
|
211
138
|
info('Aborted.');
|
|
212
139
|
return;
|
|
213
140
|
}
|
|
214
141
|
}
|
|
215
|
-
|
|
216
|
-
.from('sales')
|
|
217
|
-
.delete()
|
|
218
|
-
.eq('id', saleId)
|
|
219
|
-
.eq('user', user.id);
|
|
220
|
-
if (deleteError)
|
|
221
|
-
throw deleteError;
|
|
142
|
+
await deleteSale(supabase, user.id, saleId);
|
|
222
143
|
success(`Sale ${saleId} deleted.`);
|
|
223
144
|
}));
|
|
224
145
|
return sales;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sales.js","sourceRoot":"","sources":["../../src/commands/sales.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sales.js","sourceRoot":"","sources":["../../src/commands/sales.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,44 +1,10 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
name: string | null;
|
|
7
|
-
processing: string | null;
|
|
8
|
-
region: string | null;
|
|
9
|
-
cupping_notes: string | null;
|
|
10
|
-
ai_tasting_notes: unknown | null;
|
|
11
|
-
ai_description: string | null;
|
|
12
|
-
}
|
|
13
|
-
export interface UserTastingNotes {
|
|
14
|
-
source: 'user';
|
|
15
|
-
inventoryId: number;
|
|
16
|
-
catalogId: number | null;
|
|
17
|
-
cupping_notes: string | null;
|
|
18
|
-
notes: string | null;
|
|
19
|
-
}
|
|
20
|
-
export interface TastingResult {
|
|
21
|
-
beanId: number;
|
|
22
|
-
filter: TastingFilter;
|
|
23
|
-
supplier: SupplierTastingNotes | null;
|
|
24
|
-
user: UserTastingNotes | null;
|
|
25
|
-
}
|
|
26
|
-
export interface CuppingNotes {
|
|
27
|
-
aroma?: number;
|
|
28
|
-
body?: number;
|
|
29
|
-
acidity?: number;
|
|
30
|
-
sweetness?: number;
|
|
31
|
-
aftertaste?: number;
|
|
32
|
-
brew_method?: string;
|
|
33
|
-
notes?: string;
|
|
34
|
-
rated_at?: string;
|
|
35
|
-
}
|
|
36
|
-
/** Validate a cupping score is an integer in [1, 5]. */
|
|
37
|
-
export declare function isValidCuppingScore(value: number): boolean;
|
|
38
|
-
/** Parse and validate a cupping score flag value. */
|
|
39
|
-
export declare function parseCuppingScore(raw: string, flag: string): number;
|
|
2
|
+
import { isValidCuppingScore, parseCuppingScore } from '../lib/tasting.js';
|
|
3
|
+
import type { TastingFilter, TastingData, CuppingNotes } from '../lib/tasting.js';
|
|
4
|
+
export type { TastingFilter, TastingData, CuppingNotes };
|
|
5
|
+
export { isValidCuppingScore, parseCuppingScore };
|
|
40
6
|
/**
|
|
41
|
-
* `
|
|
7
|
+
* `purvey tasting` — View and record tasting notes for a bean.
|
|
42
8
|
* Combines supplier notes from coffee_catalog with user notes from green_coffee_inv.
|
|
43
9
|
* Requires authentication.
|
|
44
10
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasting.d.ts","sourceRoot":"","sources":["../../src/commands/tasting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"tasting.d.ts","sourceRoot":"","sources":["../../src/commands/tasting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAGL,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIlF,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,CAAC;AAIlD;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CA8G7C"}
|
package/dist/commands/tasting.js
CHANGED
|
@@ -2,22 +2,11 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { createAuthenticatedClient } from '../lib/supabase.js';
|
|
3
3
|
import { outputData, info, success } from '../lib/output.js';
|
|
4
4
|
import { withErrorHandling, AuthError, PrvrsError } from '../lib/errors.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export function isValidCuppingScore(value) {
|
|
8
|
-
return Number.isInteger(value) && value >= 1 && value <= 5;
|
|
9
|
-
}
|
|
10
|
-
/** Parse and validate a cupping score flag value. */
|
|
11
|
-
export function parseCuppingScore(raw, flag) {
|
|
12
|
-
const n = parseInt(raw, 10);
|
|
13
|
-
if (isNaN(n) || !isValidCuppingScore(n)) {
|
|
14
|
-
throw new PrvrsError('INVALID_ARGUMENT', `--${flag} must be an integer between 1 and 5 (got "${raw}").`);
|
|
15
|
-
}
|
|
16
|
-
return n;
|
|
17
|
-
}
|
|
5
|
+
import { getTastingNotes, rateCoffee, isValidCuppingScore, parseCuppingScore, } from '../lib/tasting.js';
|
|
6
|
+
export { isValidCuppingScore, parseCuppingScore };
|
|
18
7
|
// ─── Command builder ──────────────────────────────────────────────────────────
|
|
19
8
|
/**
|
|
20
|
-
* `
|
|
9
|
+
* `purvey tasting` — View and record tasting notes for a bean.
|
|
21
10
|
* Combines supplier notes from coffee_catalog with user notes from green_coffee_inv.
|
|
22
11
|
* Requires authentication.
|
|
23
12
|
*/
|
|
@@ -41,61 +30,9 @@ export function buildTastingCommand() {
|
|
|
41
30
|
const supabase = await createAuthenticatedClient();
|
|
42
31
|
const { data: { user }, } = await supabase.auth.getUser();
|
|
43
32
|
if (!user) {
|
|
44
|
-
throw new AuthError('Not logged in. Run `
|
|
45
|
-
}
|
|
46
|
-
const result = {
|
|
47
|
-
beanId: catalogId,
|
|
48
|
-
filter,
|
|
49
|
-
supplier: null,
|
|
50
|
-
user: null,
|
|
51
|
-
};
|
|
52
|
-
// ── Supplier notes (from coffee_catalog) ─────────────────────────
|
|
53
|
-
if (filter === 'supplier' || filter === 'both') {
|
|
54
|
-
const { data: catalogRow, error: catalogError } = await supabase
|
|
55
|
-
.from('coffee_catalog')
|
|
56
|
-
.select('id, name, processing, region, source, cupping_notes, ai_tasting_notes, ai_description')
|
|
57
|
-
.eq('id', catalogId)
|
|
58
|
-
.single();
|
|
59
|
-
if (catalogError && catalogError.code !== 'PGRST116') {
|
|
60
|
-
throw catalogError;
|
|
61
|
-
}
|
|
62
|
-
if (catalogRow) {
|
|
63
|
-
result.supplier = {
|
|
64
|
-
source: 'supplier',
|
|
65
|
-
catalogId: catalogRow.id,
|
|
66
|
-
name: catalogRow.name ?? null,
|
|
67
|
-
processing: catalogRow.processing ?? null,
|
|
68
|
-
region: catalogRow.region ?? null,
|
|
69
|
-
cupping_notes: catalogRow.cupping_notes ?? null,
|
|
70
|
-
ai_tasting_notes: catalogRow.ai_tasting_notes ?? null,
|
|
71
|
-
ai_description: catalogRow.ai_description ?? null,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// ── User notes (from green_coffee_inv) ───────────────────────────
|
|
76
|
-
if (filter === 'user' || filter === 'both') {
|
|
77
|
-
// A user might have multiple inventory items from the same catalog entry
|
|
78
|
-
const { data: invRows, error: invError } = await supabase
|
|
79
|
-
.from('green_coffee_inv')
|
|
80
|
-
.select('id, catalog_id, cupping_notes, notes')
|
|
81
|
-
.eq('catalog_id', catalogId)
|
|
82
|
-
.eq('user', user.id)
|
|
83
|
-
.order('id', { ascending: false })
|
|
84
|
-
.limit(1);
|
|
85
|
-
if (invError)
|
|
86
|
-
throw invError;
|
|
87
|
-
if (invRows && invRows.length > 0) {
|
|
88
|
-
const row = invRows[0];
|
|
89
|
-
result.user = {
|
|
90
|
-
source: 'user',
|
|
91
|
-
inventoryId: row.id,
|
|
92
|
-
catalogId: row.catalog_id ?? null,
|
|
93
|
-
cupping_notes: row.cupping_notes ?? null,
|
|
94
|
-
notes: row.notes ?? null,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
33
|
+
throw new AuthError('Not logged in. Run `purvey auth login` first.');
|
|
97
34
|
}
|
|
98
|
-
|
|
35
|
+
const result = await getTastingNotes(supabase, user.id, catalogId, filter);
|
|
99
36
|
if (result.supplier === null && result.user === null) {
|
|
100
37
|
info(`No tasting notes found for bean ID ${catalogId} (filter: ${filter}).`);
|
|
101
38
|
return;
|
|
@@ -119,53 +56,26 @@ export function buildTastingCommand() {
|
|
|
119
56
|
if (isNaN(inventoryId)) {
|
|
120
57
|
throw new PrvrsError('INVALID_ARGUMENT', `Invalid bean ID: "${beanId}". Pass a green_coffee_inv ID.`);
|
|
121
58
|
}
|
|
122
|
-
// Parse and validate all scores
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
aftertaste: parseCuppingScore(opts.aftertaste, 'aftertaste'),
|
|
129
|
-
rated_at: new Date().toISOString(),
|
|
130
|
-
};
|
|
131
|
-
if (opts.brewMethod !== undefined) {
|
|
132
|
-
cupping.brew_method = opts.brewMethod;
|
|
133
|
-
}
|
|
134
|
-
if (opts.notes !== undefined) {
|
|
135
|
-
cupping.notes = opts.notes;
|
|
136
|
-
}
|
|
59
|
+
// Parse and validate all scores (CLI strings → numbers)
|
|
60
|
+
const aroma = parseCuppingScore(opts.aroma, 'aroma');
|
|
61
|
+
const body = parseCuppingScore(opts.body, 'body');
|
|
62
|
+
const acidity = parseCuppingScore(opts.acidity, 'acidity');
|
|
63
|
+
const sweetness = parseCuppingScore(opts.sweetness, 'sweetness');
|
|
64
|
+
const aftertaste = parseCuppingScore(opts.aftertaste, 'aftertaste');
|
|
137
65
|
const supabase = await createAuthenticatedClient();
|
|
138
66
|
const { data: { user }, } = await supabase.auth.getUser();
|
|
139
67
|
if (!user) {
|
|
140
|
-
throw new AuthError('Not logged in. Run `
|
|
141
|
-
}
|
|
142
|
-
// Verify ownership of the inventory item
|
|
143
|
-
const { data: existing, error: fetchError } = await supabase
|
|
144
|
-
.from('green_coffee_inv')
|
|
145
|
-
.select('id, catalog_id')
|
|
146
|
-
.eq('id', inventoryId)
|
|
147
|
-
.eq('user', user.id)
|
|
148
|
-
.single();
|
|
149
|
-
if (fetchError || !existing) {
|
|
150
|
-
throw new AuthError(`Inventory item ${inventoryId} not found or does not belong to you.`);
|
|
68
|
+
throw new AuthError('Not logged in. Run `purvey auth login` first.');
|
|
151
69
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// Re-fetch the updated row
|
|
162
|
-
const { data, error } = await supabase
|
|
163
|
-
.from('green_coffee_inv')
|
|
164
|
-
.select('id, catalog_id, cupping_notes, notes, last_updated')
|
|
165
|
-
.eq('id', inventoryId)
|
|
166
|
-
.single();
|
|
167
|
-
if (error)
|
|
168
|
-
throw error;
|
|
70
|
+
const data = await rateCoffee(supabase, user.id, inventoryId, {
|
|
71
|
+
aroma,
|
|
72
|
+
body,
|
|
73
|
+
acidity,
|
|
74
|
+
sweetness,
|
|
75
|
+
aftertaste,
|
|
76
|
+
brewMethod: opts.brewMethod,
|
|
77
|
+
notes: opts.notes,
|
|
78
|
+
});
|
|
169
79
|
success(`Cupping notes saved for inventory item ${inventoryId}.`);
|
|
170
80
|
outputData(data, globalOpts);
|
|
171
81
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasting.js","sourceRoot":"","sources":["../../src/commands/tasting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"tasting.js","sourceRoot":"","sources":["../../src/commands/tasting.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -22,36 +22,36 @@ catch {
|
|
|
22
22
|
}
|
|
23
23
|
const program = new Command();
|
|
24
24
|
program
|
|
25
|
-
.name('
|
|
25
|
+
.name('purvey')
|
|
26
26
|
.description('The official CLI for purveyors.io — coffee intelligence from your terminal')
|
|
27
27
|
.version(version, '-v, --version', 'Print version')
|
|
28
28
|
.option('--pretty', 'Pretty-print JSON output with colors')
|
|
29
29
|
.option('--csv', 'Output results as CSV (useful for piping to spreadsheets)')
|
|
30
30
|
.addHelpText('after', `
|
|
31
31
|
Examples:
|
|
32
|
-
$
|
|
33
|
-
$
|
|
34
|
-
$
|
|
35
|
-
$
|
|
36
|
-
$
|
|
37
|
-
$
|
|
38
|
-
$
|
|
39
|
-
$
|
|
40
|
-
$
|
|
41
|
-
$
|
|
42
|
-
$
|
|
43
|
-
$
|
|
44
|
-
$
|
|
45
|
-
$
|
|
46
|
-
$
|
|
47
|
-
$
|
|
48
|
-
$
|
|
49
|
-
$
|
|
50
|
-
$
|
|
51
|
-
$
|
|
52
|
-
$
|
|
32
|
+
$ purvey auth login # Authenticate via Google
|
|
33
|
+
$ purvey auth status # Check login state
|
|
34
|
+
$ purvey catalog search --origin Ethiopia --stocked
|
|
35
|
+
$ purvey catalog get 42
|
|
36
|
+
$ purvey catalog stats
|
|
37
|
+
$ purvey inventory list --stocked
|
|
38
|
+
$ purvey inventory get 7
|
|
39
|
+
$ purvey inventory add --catalog-id 42 --qty 5 --cost 28.50
|
|
40
|
+
$ purvey inventory update 7 --stocked true
|
|
41
|
+
$ purvey inventory delete 7
|
|
42
|
+
$ purvey roast list --limit 5
|
|
43
|
+
$ purvey roast get 123 --include-temps
|
|
44
|
+
$ purvey roast create --coffee-id 7 --batch-name "Ethiopia Guji" --oz-in 16
|
|
45
|
+
$ purvey roast delete 123
|
|
46
|
+
$ purvey sales list
|
|
47
|
+
$ purvey sales record --roast-id 123 --oz 12 --price 22.00
|
|
48
|
+
$ purvey sales update 5 --price 24.00
|
|
49
|
+
$ purvey sales delete 5
|
|
50
|
+
$ purvey tasting get 42 --filter both
|
|
51
|
+
$ purvey tasting rate 7 --aroma 4 --body 3 --acidity 5 --sweetness 4 --aftertaste 4
|
|
52
|
+
$ purvey --help # Show this help
|
|
53
53
|
|
|
54
|
-
Docs: https://
|
|
54
|
+
Docs: https://github.com/reedwhetstone/purveyors-cli
|
|
55
55
|
`);
|
|
56
56
|
// Register subcommands
|
|
57
57
|
program.addCommand(buildAuthCommand());
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,4CAA4C;AAC5C,IAAI,OAAO,GAAG,OAAO,CAAC;AACtB,IAAI,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACrF,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;AACnC,CAAC;AAAC,MAAM,CAAC;IACP,gCAAgC;AAClC,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,4CAA4C;AAC5C,IAAI,OAAO,GAAG,OAAO,CAAC;AACtB,IAAI,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACrF,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;AACnC,CAAC;AAAC,MAAM,CAAC;IACP,gCAAgC;AAClC,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,4EAA4E,CAAC;KACzF,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,eAAe,CAAC;KAClD,MAAM,CAAC,UAAU,EAAE,sCAAsC,CAAC;KAC1D,MAAM,CAAC,OAAO,EAAE,2DAA2D,CAAC;KAC5E,WAAW,CACV,OAAO,EACP;;;;;;;;;;;;;;;;;;;;;;;;;CAyBH,CACE,CAAC;AAEJ,uBAAuB;AACvB,OAAO,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC;AACvC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;AAC1C,OAAO,CAAC,UAAU,CAAC,qBAAqB,EAAE,CAAC,CAAC;AAC5C,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC;AAE1C,qBAAqB;AACrB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtD,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
export interface TemperatureRow {
|
|
3
|
+
roast_id: number;
|
|
4
|
+
time_seconds: number;
|
|
5
|
+
bean_temp?: number | null;
|
|
6
|
+
environmental_temp?: number | null;
|
|
7
|
+
ambient_temp?: number | null;
|
|
8
|
+
ror_bean_temp?: number | null;
|
|
9
|
+
data_source: string;
|
|
10
|
+
}
|
|
11
|
+
export interface EventRow {
|
|
12
|
+
roast_id: number;
|
|
13
|
+
time_seconds: number;
|
|
14
|
+
event_type: number;
|
|
15
|
+
event_value: string | null;
|
|
16
|
+
event_string: string;
|
|
17
|
+
category: string;
|
|
18
|
+
subcategory: string;
|
|
19
|
+
user_generated: boolean;
|
|
20
|
+
automatic: boolean;
|
|
21
|
+
notes?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const BATCH_SIZE = 100;
|
|
24
|
+
/**
|
|
25
|
+
* Clear existing roast data scoped to artisan_import source.
|
|
26
|
+
* Deletes temperature rows by data_source and event rows by category.
|
|
27
|
+
*/
|
|
28
|
+
export declare function clearRoastData(supabase: SupabaseClient, roastId: number, source: 'artisan_import' | 'live'): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Batch-insert temperature rows into roast_temperatures.
|
|
31
|
+
*/
|
|
32
|
+
export declare function insertTemperatures(supabase: SupabaseClient, entries: TemperatureRow[]): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Batch-insert event rows into roast_events.
|
|
35
|
+
*/
|
|
36
|
+
export declare function insertEvents(supabase: SupabaseClient, entries: EventRow[]): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=db.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/lib/artisan/db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,eAAO,MAAM,UAAU,MAAM,CAAC;AAI9B;;;GAGG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gBAAgB,GAAG,MAAM,GAChC,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,cAAc,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/F"}
|