@purveyors/cli 0.2.1 → 0.4.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.
Files changed (95) hide show
  1. package/dist/commands/catalog.d.ts +4 -56
  2. package/dist/commands/catalog.d.ts.map +1 -1
  3. package/dist/commands/catalog.js +14 -81
  4. package/dist/commands/catalog.js.map +1 -1
  5. package/dist/commands/config.d.ts +7 -0
  6. package/dist/commands/config.d.ts.map +1 -0
  7. package/dist/commands/config.js +82 -0
  8. package/dist/commands/config.js.map +1 -0
  9. package/dist/commands/inventory.d.ts +2 -25
  10. package/dist/commands/inventory.d.ts.map +1 -1
  11. package/dist/commands/inventory.js +109 -149
  12. package/dist/commands/inventory.js.map +1 -1
  13. package/dist/commands/roast.d.ts +2 -41
  14. package/dist/commands/roast.d.ts.map +1 -1
  15. package/dist/commands/roast.js +445 -126
  16. package/dist/commands/roast.js.map +1 -1
  17. package/dist/commands/sales.d.ts +2 -10
  18. package/dist/commands/sales.d.ts.map +1 -1
  19. package/dist/commands/sales.js +100 -112
  20. package/dist/commands/sales.js.map +1 -1
  21. package/dist/commands/tasting.d.ts +4 -38
  22. package/dist/commands/tasting.d.ts.map +1 -1
  23. package/dist/commands/tasting.js +93 -116
  24. package/dist/commands/tasting.js.map +1 -1
  25. package/dist/index.js +8 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/lib/ai.d.ts +36 -0
  28. package/dist/lib/ai.d.ts.map +1 -0
  29. package/dist/lib/ai.js +38 -0
  30. package/dist/lib/ai.js.map +1 -0
  31. package/dist/lib/artisan/db.d.ts +37 -0
  32. package/dist/lib/artisan/db.d.ts.map +1 -0
  33. package/dist/lib/artisan/db.js +51 -0
  34. package/dist/lib/artisan/db.js.map +1 -0
  35. package/dist/lib/artisan/import.d.ts +16 -0
  36. package/dist/lib/artisan/import.d.ts.map +1 -0
  37. package/dist/lib/artisan/import.js +447 -0
  38. package/dist/lib/artisan/import.js.map +1 -0
  39. package/dist/lib/artisan/index.d.ts +9 -0
  40. package/dist/lib/artisan/index.d.ts.map +1 -0
  41. package/dist/lib/artisan/index.js +7 -0
  42. package/dist/lib/artisan/index.js.map +1 -0
  43. package/dist/lib/artisan/parser.d.ts +19 -0
  44. package/dist/lib/artisan/parser.d.ts.map +1 -0
  45. package/dist/lib/artisan/parser.js +376 -0
  46. package/dist/lib/artisan/parser.js.map +1 -0
  47. package/dist/lib/artisan/temperature.d.ts +52 -0
  48. package/dist/lib/artisan/temperature.d.ts.map +1 -0
  49. package/dist/lib/artisan/temperature.js +101 -0
  50. package/dist/lib/artisan/temperature.js.map +1 -0
  51. package/dist/lib/artisan/types.d.ts +195 -0
  52. package/dist/lib/artisan/types.d.ts.map +1 -0
  53. package/dist/lib/artisan/types.js +35 -0
  54. package/dist/lib/artisan/types.js.map +1 -0
  55. package/dist/lib/artisan/validator.d.ts +14 -0
  56. package/dist/lib/artisan/validator.d.ts.map +1 -0
  57. package/dist/lib/artisan/validator.js +228 -0
  58. package/dist/lib/artisan/validator.js.map +1 -0
  59. package/dist/lib/catalog.d.ts +87 -0
  60. package/dist/lib/catalog.d.ts.map +1 -0
  61. package/dist/lib/catalog.js +111 -0
  62. package/dist/lib/catalog.js.map +1 -0
  63. package/dist/lib/config.d.ts +26 -0
  64. package/dist/lib/config.d.ts.map +1 -1
  65. package/dist/lib/config.js +59 -0
  66. package/dist/lib/config.js.map +1 -1
  67. package/dist/lib/index.d.ts +6 -0
  68. package/dist/lib/index.d.ts.map +1 -0
  69. package/dist/lib/index.js +11 -0
  70. package/dist/lib/index.js.map +1 -0
  71. package/dist/lib/interactive/forms.d.ts +33 -0
  72. package/dist/lib/interactive/forms.d.ts.map +1 -0
  73. package/dist/lib/interactive/forms.js +139 -0
  74. package/dist/lib/interactive/forms.js.map +1 -0
  75. package/dist/lib/interactive/watch.d.ts +66 -0
  76. package/dist/lib/interactive/watch.d.ts.map +1 -0
  77. package/dist/lib/interactive/watch.js +494 -0
  78. package/dist/lib/interactive/watch.js.map +1 -0
  79. package/dist/lib/inventory.d.ts +80 -0
  80. package/dist/lib/inventory.d.ts.map +1 -0
  81. package/dist/lib/inventory.js +205 -0
  82. package/dist/lib/inventory.js.map +1 -0
  83. package/dist/lib/roast.d.ts +127 -0
  84. package/dist/lib/roast.d.ts.map +1 -0
  85. package/dist/lib/roast.js +284 -0
  86. package/dist/lib/roast.js.map +1 -0
  87. package/dist/lib/sales.d.ts +53 -0
  88. package/dist/lib/sales.d.ts.map +1 -0
  89. package/dist/lib/sales.js +155 -0
  90. package/dist/lib/sales.js.map +1 -0
  91. package/dist/lib/tasting.d.ts +76 -0
  92. package/dist/lib/tasting.d.ts.map +1 -0
  93. package/dist/lib/tasting.js +136 -0
  94. package/dist/lib/tasting.js.map +1 -0
  95. package/package.json +15 -2
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ import type { SupabaseClient } from '@supabase/supabase-js';
3
+ export interface Sale {
4
+ id: number;
5
+ roast_id: number | null;
6
+ oz_sold: number | null;
7
+ sale_price: number | null;
8
+ buyer: string | null;
9
+ sell_date: string | null;
10
+ user: string;
11
+ last_updated: string;
12
+ }
13
+ export declare const SALE_SELECT = "id, roast_id, oz_sold, sale_price, buyer, sell_date, user, last_updated";
14
+ export declare const listSalesSchema: z.ZodObject<{
15
+ limit: z.ZodDefault<z.ZodNumber>;
16
+ }, z.core.$strip>;
17
+ export type ListSalesInput = z.input<typeof listSalesSchema>;
18
+ export declare const recordSaleSchema: z.ZodObject<{
19
+ roastId: z.ZodNumber;
20
+ oz: z.ZodNumber;
21
+ price: z.ZodNumber;
22
+ buyer: z.ZodOptional<z.ZodString>;
23
+ sellDate: z.ZodOptional<z.ZodString>;
24
+ }, z.core.$strip>;
25
+ export type RecordSaleInput = z.input<typeof recordSaleSchema>;
26
+ export declare const updateSaleSchema: z.ZodObject<{
27
+ oz: z.ZodOptional<z.ZodNumber>;
28
+ price: z.ZodOptional<z.ZodNumber>;
29
+ buyer: z.ZodOptional<z.ZodString>;
30
+ sellDate: z.ZodOptional<z.ZodString>;
31
+ }, z.core.$strip>;
32
+ export type UpdateSaleInput = z.input<typeof updateSaleSchema>;
33
+ export declare const deleteSaleSchema: z.ZodObject<{
34
+ id: z.ZodNumber;
35
+ }, z.core.$strip>;
36
+ export type DeleteSaleInput = z.input<typeof deleteSaleSchema>;
37
+ /**
38
+ * List sales for a user (newest first, with roast profile join).
39
+ */
40
+ export declare function listSales(supabase: SupabaseClient, userId: string, opts?: ListSalesInput): Promise<Sale[]>;
41
+ /**
42
+ * Record a new sale (roastId must belong to userId).
43
+ */
44
+ export declare function recordSale(supabase: SupabaseClient, userId: string, input: RecordSaleInput): Promise<Sale>;
45
+ /**
46
+ * Update an existing sale (must belong to userId).
47
+ */
48
+ export declare function updateSale(supabase: SupabaseClient, userId: string, id: number, input: UpdateSaleInput): Promise<Sale>;
49
+ /**
50
+ * Delete a sale (must belong to userId).
51
+ */
52
+ export declare function deleteSale(supabase: SupabaseClient, userId: string, id: number): Promise<void>;
53
+ //# sourceMappingURL=sales.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sales.d.ts","sourceRoot":"","sources":["../../src/lib/sales.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAK5D,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAID,eAAO,MAAM,WAAW,4EACmD,CAAC;AAI5E,eAAO,MAAM,eAAe;;iBAE1B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE7D,eAAO,MAAM,gBAAgB;;;;;;iBAM3B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE/D,eAAO,MAAM,gBAAgB;;;;;iBASzB,CAAC;AAEL,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE/D,eAAO,MAAM,gBAAgB;;iBAE3B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAI/D;;GAEG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,IAAI,EAAE,CAAC,CAajB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,IAAI,CAAC,CA6Cf;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,IAAI,CAAC,CA+Cf;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CAsBf"}
@@ -0,0 +1,155 @@
1
+ import { z } from 'zod';
2
+ import { AuthError, PrvrsError } from './errors.js';
3
+ // ─── Shared select columns ────────────────────────────────────────────────────
4
+ export const SALE_SELECT = 'id, roast_id, oz_sold, sale_price, buyer, sell_date, user, last_updated';
5
+ // ─── Zod schemas ──────────────────────────────────────────────────────────────
6
+ export const listSalesSchema = z.object({
7
+ limit: z.number().int().min(1).default(20),
8
+ });
9
+ export const recordSaleSchema = z.object({
10
+ roastId: z.number().int().positive(),
11
+ oz: z.number().positive(),
12
+ price: z.number().min(0),
13
+ buyer: z.string().optional(),
14
+ sellDate: z.string().optional(),
15
+ });
16
+ export const updateSaleSchema = z
17
+ .object({
18
+ oz: z.number().positive().optional(),
19
+ price: z.number().min(0).optional(),
20
+ buyer: z.string().optional(),
21
+ sellDate: z.string().optional(),
22
+ })
23
+ .refine((v) => Object.keys(v).some((k) => v[k] !== undefined), {
24
+ message: 'No update fields provided. Pass at least one of: oz, price, buyer, sellDate.',
25
+ });
26
+ export const deleteSaleSchema = z.object({
27
+ id: z.number().int().positive(),
28
+ });
29
+ // ─── Pure lib functions ───────────────────────────────────────────────────────
30
+ /**
31
+ * List sales for a user (newest first, with roast profile join).
32
+ */
33
+ export async function listSales(supabase, userId, opts = {}) {
34
+ const parsed = listSalesSchema.parse(opts);
35
+ const { data, error } = await supabase
36
+ .from('sales')
37
+ .select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
38
+ .eq('user', userId)
39
+ .order('sell_date', { ascending: false })
40
+ .limit(parsed.limit);
41
+ if (error)
42
+ throw error;
43
+ return (data ?? []);
44
+ }
45
+ /**
46
+ * Record a new sale (roastId must belong to userId).
47
+ */
48
+ export async function recordSale(supabase, userId, input) {
49
+ const parsed = recordSaleSchema.parse(input);
50
+ const todayIso = () => new Date().toISOString().slice(0, 10);
51
+ // Verify the roast profile belongs to the user
52
+ const { data: roastExists, error: roastError } = await supabase
53
+ .from('roast_profiles')
54
+ .select('roast_id')
55
+ .eq('roast_id', parsed.roastId)
56
+ .eq('user', userId)
57
+ .single();
58
+ if (roastError || !roastExists) {
59
+ throw new AuthError(`Roast profile ${parsed.roastId} not found or does not belong to you.`);
60
+ }
61
+ const insertPayload = {
62
+ user: userId,
63
+ roast_id: parsed.roastId,
64
+ oz_sold: parsed.oz,
65
+ sale_price: parsed.price,
66
+ sell_date: parsed.sellDate ?? todayIso(),
67
+ };
68
+ if (parsed.buyer !== undefined)
69
+ insertPayload.buyer = parsed.buyer;
70
+ const { data: inserted, error: insertError } = await supabase
71
+ .from('sales')
72
+ .insert(insertPayload)
73
+ .select('id')
74
+ .single();
75
+ if (insertError)
76
+ throw insertError;
77
+ // Re-fetch the full row with roast join
78
+ const { data, error } = await supabase
79
+ .from('sales')
80
+ .select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
81
+ .eq('id', inserted.id)
82
+ .single();
83
+ if (error)
84
+ throw error;
85
+ return data;
86
+ }
87
+ /**
88
+ * Update an existing sale (must belong to userId).
89
+ */
90
+ export async function updateSale(supabase, userId, id, input) {
91
+ deleteSaleSchema.parse({ id });
92
+ const parsed = updateSaleSchema.parse(input);
93
+ // Verify ownership
94
+ const { data: existing, error: fetchError } = await supabase
95
+ .from('sales')
96
+ .select('id')
97
+ .eq('id', id)
98
+ .eq('user', userId)
99
+ .single();
100
+ if (fetchError || !existing) {
101
+ throw new AuthError(`Sale ${id} not found or does not belong to you.`);
102
+ }
103
+ const updates = {};
104
+ if (parsed.oz !== undefined)
105
+ updates.oz_sold = parsed.oz;
106
+ if (parsed.price !== undefined)
107
+ updates.sale_price = parsed.price;
108
+ if (parsed.buyer !== undefined)
109
+ updates.buyer = parsed.buyer;
110
+ if (parsed.sellDate !== undefined)
111
+ updates.sell_date = parsed.sellDate;
112
+ if (Object.keys(updates).length === 0) {
113
+ throw new PrvrsError('INVALID_ARGUMENT', 'No update fields provided. Pass at least one of: oz, price, buyer, sellDate.');
114
+ }
115
+ const { error: updateError } = await supabase
116
+ .from('sales')
117
+ .update(updates)
118
+ .eq('id', id)
119
+ .eq('user', userId);
120
+ if (updateError)
121
+ throw updateError;
122
+ // Re-fetch the updated row
123
+ const { data, error } = await supabase
124
+ .from('sales')
125
+ .select(`${SALE_SELECT}, roast_profiles!roast_id (batch_name, coffee_name)`)
126
+ .eq('id', id)
127
+ .single();
128
+ if (error)
129
+ throw error;
130
+ return data;
131
+ }
132
+ /**
133
+ * Delete a sale (must belong to userId).
134
+ */
135
+ export async function deleteSale(supabase, userId, id) {
136
+ deleteSaleSchema.parse({ id });
137
+ // Verify ownership
138
+ const { data: existing, error: fetchError } = await supabase
139
+ .from('sales')
140
+ .select('id')
141
+ .eq('id', id)
142
+ .eq('user', userId)
143
+ .single();
144
+ if (fetchError || !existing) {
145
+ throw new AuthError(`Sale ${id} not found or does not belong to you.`);
146
+ }
147
+ const { error: deleteError } = await supabase
148
+ .from('sales')
149
+ .delete()
150
+ .eq('id', id)
151
+ .eq('user', userId);
152
+ if (deleteError)
153
+ throw deleteError;
154
+ }
155
+ //# sourceMappingURL=sales.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sales.js","sourceRoot":"","sources":["../../src/lib/sales.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAepD,iFAAiF;AAEjF,MAAM,CAAC,MAAM,WAAW,GACtB,yEAAyE,CAAC;AAE5E,iFAAiF;AAEjF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC3C,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACpC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,CAAC;IACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACpC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC;KACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAmB,CAAC,KAAK,SAAS,CAAC,EAAE;IAC/E,OAAO,EAAE,8EAA8E;CACxF,CAAC,CAAC;AAIL,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAIH,iFAAiF;AAEjF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAwB,EACxB,MAAc,EACd,OAAuB,EAAE;IAEzB,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE3C,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ;SACnC,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,GAAG,WAAW,qDAAqD,CAAC;SAC3E,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;SAClB,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;SACxC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEvB,IAAI,KAAK;QAAE,MAAM,KAAK,CAAC;IAEvB,OAAO,CAAC,IAAI,IAAI,EAAE,CAAW,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,QAAwB,EACxB,MAAc,EACd,KAAsB;IAEtB,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE7C,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAE7D,+CAA+C;IAC/C,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,QAAQ;SAC5D,IAAI,CAAC,gBAAgB,CAAC;SACtB,MAAM,CAAC,UAAU,CAAC;SAClB,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC;SAC9B,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC;QAC/B,MAAM,IAAI,SAAS,CAAC,iBAAiB,MAAM,CAAC,OAAO,uCAAuC,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,aAAa,GAA4B;QAC7C,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,MAAM,CAAC,OAAO;QACxB,OAAO,EAAE,MAAM,CAAC,EAAE;QAClB,UAAU,EAAE,MAAM,CAAC,KAAK;QACxB,SAAS,EAAE,MAAM,CAAC,QAAQ,IAAI,QAAQ,EAAE;KACzC,CAAC;IAEF,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;QAAE,aAAa,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAEnE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ;SAC1D,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,IAAI,CAAC;SACZ,MAAM,EAAE,CAAC;IAEZ,IAAI,WAAW;QAAE,MAAM,WAAW,CAAC;IAEnC,wCAAwC;IACxC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ;SACnC,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,GAAG,WAAW,qDAAqD,CAAC;SAC3E,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;SACrB,MAAM,EAAE,CAAC;IAEZ,IAAI,KAAK;QAAE,MAAM,KAAK,CAAC;IAEvB,OAAO,IAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,QAAwB,EACxB,MAAc,EACd,EAAU,EACV,KAAsB;IAEtB,gBAAgB,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE7C,mBAAmB;IACnB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,QAAQ;SACzD,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,IAAI,CAAC;SACZ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;SACZ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,SAAS,CAAC,QAAQ,EAAE,uCAAuC,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,OAAO,GAA4B,EAAE,CAAC;IAC5C,IAAI,MAAM,CAAC,EAAE,KAAK,SAAS;QAAE,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;IACzD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC;IAClE,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC7D,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC;IAEvE,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,UAAU,CAClB,kBAAkB,EAClB,8EAA8E,CAC/E,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ;SAC1C,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,OAAO,CAAC;SACf,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;SACZ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtB,IAAI,WAAW;QAAE,MAAM,WAAW,CAAC;IAEnC,2BAA2B;IAC3B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ;SACnC,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,GAAG,WAAW,qDAAqD,CAAC;SAC3E,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;SACZ,MAAM,EAAE,CAAC;IAEZ,IAAI,KAAK;QAAE,MAAM,KAAK,CAAC;IAEvB,OAAO,IAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,QAAwB,EACxB,MAAc,EACd,EAAU;IAEV,gBAAgB,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAE/B,mBAAmB;IACnB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,QAAQ;SACzD,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,CAAC,IAAI,CAAC;SACZ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;SACZ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,SAAS,CAAC,QAAQ,EAAE,uCAAuC,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ;SAC1C,IAAI,CAAC,OAAO,CAAC;SACb,MAAM,EAAE;SACR,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;SACZ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtB,IAAI,WAAW;QAAE,MAAM,WAAW,CAAC;AACrC,CAAC"}
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ import type { SupabaseClient } from '@supabase/supabase-js';
3
+ import type { InventoryItem } from './inventory.js';
4
+ export type TastingFilter = 'user' | 'supplier' | 'both';
5
+ export interface SupplierTastingNotes {
6
+ source: 'supplier';
7
+ catalogId: number;
8
+ name: string | null;
9
+ processing: string | null;
10
+ region: string | null;
11
+ cupping_notes: string | null;
12
+ ai_tasting_notes: unknown | null;
13
+ ai_description: string | null;
14
+ }
15
+ export interface UserTastingNotes {
16
+ source: 'user';
17
+ inventoryId: number;
18
+ catalogId: number | null;
19
+ cupping_notes: string | null;
20
+ notes: string | null;
21
+ }
22
+ export interface TastingData {
23
+ beanId: number;
24
+ filter: TastingFilter;
25
+ supplier: SupplierTastingNotes | null;
26
+ user: UserTastingNotes | null;
27
+ }
28
+ export interface CuppingNotes {
29
+ aroma?: number;
30
+ body?: number;
31
+ acidity?: number;
32
+ sweetness?: number;
33
+ aftertaste?: number;
34
+ brew_method?: string;
35
+ notes?: string;
36
+ rated_at?: string;
37
+ }
38
+ export declare const tastingFilterSchema: z.ZodEnum<{
39
+ user: "user";
40
+ supplier: "supplier";
41
+ both: "both";
42
+ }>;
43
+ export declare const getTastingNotesSchema: z.ZodObject<{
44
+ id: z.ZodNumber;
45
+ filter: z.ZodDefault<z.ZodEnum<{
46
+ user: "user";
47
+ supplier: "supplier";
48
+ both: "both";
49
+ }>>;
50
+ }, z.core.$strip>;
51
+ export type GetTastingNotesInput = z.input<typeof getTastingNotesSchema>;
52
+ export declare const cuppingScoreSchema: z.ZodNumber;
53
+ export declare const rateCoffeeSchema: z.ZodObject<{
54
+ aroma: z.ZodNumber;
55
+ body: z.ZodNumber;
56
+ acidity: z.ZodNumber;
57
+ sweetness: z.ZodNumber;
58
+ aftertaste: z.ZodNumber;
59
+ brewMethod: z.ZodOptional<z.ZodString>;
60
+ notes: z.ZodOptional<z.ZodString>;
61
+ }, z.core.$strip>;
62
+ export type RateCoffeeInput = z.input<typeof rateCoffeeSchema>;
63
+ /** Validate a cupping score is an integer in [1, 5]. */
64
+ export declare function isValidCuppingScore(value: number): boolean;
65
+ /** Parse and validate a cupping score flag value (for CLI use). */
66
+ export declare function parseCuppingScore(raw: string, flag: string): number;
67
+ /**
68
+ * Get tasting notes for a bean by coffee_catalog ID.
69
+ * Combines supplier notes from coffee_catalog with user notes from green_coffee_inv.
70
+ */
71
+ export declare function getTastingNotes(supabase: SupabaseClient, userId: string, id: number, filter?: TastingFilter): Promise<TastingData>;
72
+ /**
73
+ * Rate a coffee by storing cupping scores on the inventory item (must belong to userId).
74
+ */
75
+ export declare function rateCoffee(supabase: SupabaseClient, userId: string, id: number, input: RateCoffeeInput): Promise<InventoryItem>;
76
+ //# sourceMappingURL=tasting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasting.d.ts","sourceRoot":"","sources":["../../src/lib/tasting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID,eAAO,MAAM,mBAAmB;;;;EAAuC,CAAC;AAExE,eAAO,MAAM,qBAAqB;;;;;;;iBAGhC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEzE,eAAO,MAAM,kBAAkB,aAAiC,CAAC;AAEjE,eAAO,MAAM,gBAAgB;;;;;;;;iBAQ3B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAI/D,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CASnE;AAID;;;GAGG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,MAAM,GAAE,aAAsB,GAC7B,OAAO,CAAC,WAAW,CAAC,CA+DtB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,aAAa,CAAC,CA6CxB"}
@@ -0,0 +1,136 @@
1
+ import { z } from 'zod';
2
+ import { AuthError, PrvrsError } from './errors.js';
3
+ // ─── Zod schemas ──────────────────────────────────────────────────────────────
4
+ export const tastingFilterSchema = z.enum(['user', 'supplier', 'both']);
5
+ export const getTastingNotesSchema = z.object({
6
+ id: z.number().int().positive(),
7
+ filter: tastingFilterSchema.default('both'),
8
+ });
9
+ export const cuppingScoreSchema = z.number().int().min(1).max(5);
10
+ export const rateCoffeeSchema = z.object({
11
+ aroma: cuppingScoreSchema,
12
+ body: cuppingScoreSchema,
13
+ acidity: cuppingScoreSchema,
14
+ sweetness: cuppingScoreSchema,
15
+ aftertaste: cuppingScoreSchema,
16
+ brewMethod: z.string().optional(),
17
+ notes: z.string().optional(),
18
+ });
19
+ // ─── Validation helpers ───────────────────────────────────────────────────────
20
+ /** Validate a cupping score is an integer in [1, 5]. */
21
+ export function isValidCuppingScore(value) {
22
+ return Number.isInteger(value) && value >= 1 && value <= 5;
23
+ }
24
+ /** Parse and validate a cupping score flag value (for CLI use). */
25
+ export function parseCuppingScore(raw, flag) {
26
+ const n = parseInt(raw, 10);
27
+ if (isNaN(n) || !isValidCuppingScore(n)) {
28
+ throw new PrvrsError('INVALID_ARGUMENT', `--${flag} must be an integer between 1 and 5 (got "${raw}").`);
29
+ }
30
+ return n;
31
+ }
32
+ // ─── Pure lib functions ───────────────────────────────────────────────────────
33
+ /**
34
+ * Get tasting notes for a bean by coffee_catalog ID.
35
+ * Combines supplier notes from coffee_catalog with user notes from green_coffee_inv.
36
+ */
37
+ export async function getTastingNotes(supabase, userId, id, filter = 'both') {
38
+ getTastingNotesSchema.parse({ id, filter });
39
+ const result = {
40
+ beanId: id,
41
+ filter,
42
+ supplier: null,
43
+ user: null,
44
+ };
45
+ // ── Supplier notes (from coffee_catalog) ─────────────────────────
46
+ if (filter === 'supplier' || filter === 'both') {
47
+ const { data: catalogRow, error: catalogError } = await supabase
48
+ .from('coffee_catalog')
49
+ .select('id, name, processing, region, source, cupping_notes, ai_tasting_notes, ai_description')
50
+ .eq('id', id)
51
+ .single();
52
+ if (catalogError && catalogError.code !== 'PGRST116') {
53
+ throw catalogError;
54
+ }
55
+ if (catalogRow) {
56
+ result.supplier = {
57
+ source: 'supplier',
58
+ catalogId: catalogRow.id,
59
+ name: catalogRow.name ?? null,
60
+ processing: catalogRow.processing ?? null,
61
+ region: catalogRow.region ?? null,
62
+ cupping_notes: catalogRow.cupping_notes ?? null,
63
+ ai_tasting_notes: catalogRow.ai_tasting_notes ?? null,
64
+ ai_description: catalogRow.ai_description ?? null,
65
+ };
66
+ }
67
+ }
68
+ // ── User notes (from green_coffee_inv) ───────────────────────────
69
+ if (filter === 'user' || filter === 'both') {
70
+ const { data: invRows, error: invError } = await supabase
71
+ .from('green_coffee_inv')
72
+ .select('id, catalog_id, cupping_notes, notes')
73
+ .eq('catalog_id', id)
74
+ .eq('user', userId)
75
+ .order('id', { ascending: false })
76
+ .limit(1);
77
+ if (invError)
78
+ throw invError;
79
+ if (invRows && invRows.length > 0) {
80
+ const row = invRows[0];
81
+ result.user = {
82
+ source: 'user',
83
+ inventoryId: row.id,
84
+ catalogId: row.catalog_id ?? null,
85
+ cupping_notes: row.cupping_notes ?? null,
86
+ notes: row.notes ?? null,
87
+ };
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+ /**
93
+ * Rate a coffee by storing cupping scores on the inventory item (must belong to userId).
94
+ */
95
+ export async function rateCoffee(supabase, userId, id, input) {
96
+ const parsed = rateCoffeeSchema.parse(input);
97
+ // Verify ownership of the inventory item
98
+ const { data: existing, error: fetchError } = await supabase
99
+ .from('green_coffee_inv')
100
+ .select('id, catalog_id')
101
+ .eq('id', id)
102
+ .eq('user', userId)
103
+ .single();
104
+ if (fetchError || !existing) {
105
+ throw new AuthError(`Inventory item ${id} not found or does not belong to you.`);
106
+ }
107
+ const cupping = {
108
+ aroma: parsed.aroma,
109
+ body: parsed.body,
110
+ acidity: parsed.acidity,
111
+ sweetness: parsed.sweetness,
112
+ aftertaste: parsed.aftertaste,
113
+ rated_at: new Date().toISOString(),
114
+ };
115
+ if (parsed.brewMethod !== undefined)
116
+ cupping.brew_method = parsed.brewMethod;
117
+ if (parsed.notes !== undefined)
118
+ cupping.notes = parsed.notes;
119
+ const { error: updateError } = await supabase
120
+ .from('green_coffee_inv')
121
+ .update({ cupping_notes: cupping })
122
+ .eq('id', id)
123
+ .eq('user', userId);
124
+ if (updateError)
125
+ throw updateError;
126
+ // Re-fetch the updated row
127
+ const { data, error } = await supabase
128
+ .from('green_coffee_inv')
129
+ .select('id, catalog_id, cupping_notes, notes, last_updated')
130
+ .eq('id', id)
131
+ .single();
132
+ if (error)
133
+ throw error;
134
+ return data;
135
+ }
136
+ //# sourceMappingURL=tasting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasting.js","sourceRoot":"","sources":["../../src/lib/tasting.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,22 @@
1
1
  {
2
2
  "name": "@purveyors/cli",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "The official CLI for purveyors.io — coffee intelligence from your terminal",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "purvey": "./dist/index.js"
8
8
  },
9
+ "exports": {
10
+ ".": "./dist/index.js",
11
+ "./catalog": "./dist/lib/catalog.js",
12
+ "./inventory": "./dist/lib/inventory.js",
13
+ "./roast": "./dist/lib/roast.js",
14
+ "./sales": "./dist/lib/sales.js",
15
+ "./tasting": "./dist/lib/tasting.js",
16
+ "./lib": "./dist/lib/index.js",
17
+ "./artisan": "./dist/lib/artisan/index.js",
18
+ "./ai": "./dist/lib/ai.js"
19
+ },
9
20
  "files": [
10
21
  "dist",
11
22
  "README.md",
@@ -39,10 +50,12 @@
39
50
  "node": ">=20"
40
51
  },
41
52
  "dependencies": {
53
+ "@clack/prompts": "^1.1.0",
42
54
  "@supabase/supabase-js": "^2.49.1",
43
55
  "chalk": "^5.4.1",
44
56
  "commander": "^13.1.0",
45
- "ora": "^8.2.0"
57
+ "ora": "^8.2.0",
58
+ "zod": "^4.3.6"
46
59
  },
47
60
  "devDependencies": {
48
61
  "@eslint/eslintrc": "^3.3.0",