@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.
Files changed (86) hide show
  1. package/README.md +23 -23
  2. package/dist/commands/auth.js +4 -4
  3. package/dist/commands/auth.js.map +1 -1
  4. package/dist/commands/catalog.d.ts +5 -57
  5. package/dist/commands/catalog.d.ts.map +1 -1
  6. package/dist/commands/catalog.js +15 -82
  7. package/dist/commands/catalog.js.map +1 -1
  8. package/dist/commands/inventory.d.ts +3 -26
  9. package/dist/commands/inventory.d.ts.map +1 -1
  10. package/dist/commands/inventory.js +53 -153
  11. package/dist/commands/inventory.js.map +1 -1
  12. package/dist/commands/roast.d.ts +3 -42
  13. package/dist/commands/roast.d.ts.map +1 -1
  14. package/dist/commands/roast.js +144 -131
  15. package/dist/commands/roast.js.map +1 -1
  16. package/dist/commands/sales.d.ts +3 -11
  17. package/dist/commands/sales.d.ts.map +1 -1
  18. package/dist/commands/sales.js +35 -114
  19. package/dist/commands/sales.js.map +1 -1
  20. package/dist/commands/tasting.d.ts +5 -39
  21. package/dist/commands/tasting.d.ts.map +1 -1
  22. package/dist/commands/tasting.js +21 -111
  23. package/dist/commands/tasting.js.map +1 -1
  24. package/dist/index.js +23 -23
  25. package/dist/index.js.map +1 -1
  26. package/dist/lib/artisan/db.d.ts +37 -0
  27. package/dist/lib/artisan/db.d.ts.map +1 -0
  28. package/dist/lib/artisan/db.js +51 -0
  29. package/dist/lib/artisan/db.js.map +1 -0
  30. package/dist/lib/artisan/import.d.ts +16 -0
  31. package/dist/lib/artisan/import.d.ts.map +1 -0
  32. package/dist/lib/artisan/import.js +447 -0
  33. package/dist/lib/artisan/import.js.map +1 -0
  34. package/dist/lib/artisan/index.d.ts +9 -0
  35. package/dist/lib/artisan/index.d.ts.map +1 -0
  36. package/dist/lib/artisan/index.js +7 -0
  37. package/dist/lib/artisan/index.js.map +1 -0
  38. package/dist/lib/artisan/parser.d.ts +19 -0
  39. package/dist/lib/artisan/parser.d.ts.map +1 -0
  40. package/dist/lib/artisan/parser.js +376 -0
  41. package/dist/lib/artisan/parser.js.map +1 -0
  42. package/dist/lib/artisan/temperature.d.ts +52 -0
  43. package/dist/lib/artisan/temperature.d.ts.map +1 -0
  44. package/dist/lib/artisan/temperature.js +101 -0
  45. package/dist/lib/artisan/temperature.js.map +1 -0
  46. package/dist/lib/artisan/types.d.ts +195 -0
  47. package/dist/lib/artisan/types.d.ts.map +1 -0
  48. package/dist/lib/artisan/types.js +35 -0
  49. package/dist/lib/artisan/types.js.map +1 -0
  50. package/dist/lib/artisan/validator.d.ts +14 -0
  51. package/dist/lib/artisan/validator.d.ts.map +1 -0
  52. package/dist/lib/artisan/validator.js +228 -0
  53. package/dist/lib/artisan/validator.js.map +1 -0
  54. package/dist/lib/catalog.d.ts +87 -0
  55. package/dist/lib/catalog.d.ts.map +1 -0
  56. package/dist/lib/catalog.js +111 -0
  57. package/dist/lib/catalog.js.map +1 -0
  58. package/dist/lib/config.d.ts +1 -0
  59. package/dist/lib/config.d.ts.map +1 -1
  60. package/dist/lib/config.js +20 -2
  61. package/dist/lib/config.js.map +1 -1
  62. package/dist/lib/errors.js +2 -2
  63. package/dist/lib/errors.js.map +1 -1
  64. package/dist/lib/index.d.ts +6 -0
  65. package/dist/lib/index.d.ts.map +1 -0
  66. package/dist/lib/index.js +11 -0
  67. package/dist/lib/index.js.map +1 -0
  68. package/dist/lib/inventory.d.ts +80 -0
  69. package/dist/lib/inventory.d.ts.map +1 -0
  70. package/dist/lib/inventory.js +205 -0
  71. package/dist/lib/inventory.js.map +1 -0
  72. package/dist/lib/roast.d.ts +127 -0
  73. package/dist/lib/roast.d.ts.map +1 -0
  74. package/dist/lib/roast.js +284 -0
  75. package/dist/lib/roast.js.map +1 -0
  76. package/dist/lib/sales.d.ts +53 -0
  77. package/dist/lib/sales.d.ts.map +1 -0
  78. package/dist/lib/sales.js +155 -0
  79. package/dist/lib/sales.js.map +1 -0
  80. package/dist/lib/supabase.js +2 -2
  81. package/dist/lib/supabase.js.map +1 -1
  82. package/dist/lib/tasting.d.ts +76 -0
  83. package/dist/lib/tasting.d.ts.map +1 -0
  84. package/dist/lib/tasting.js +136 -0
  85. package/dist/lib/tasting.js.map +1 -0
  86. package/package.json +24 -13
@@ -0,0 +1,127 @@
1
+ import { z } from 'zod';
2
+ import type { SupabaseClient } from '@supabase/supabase-js';
3
+ import type { ArtisanImportResult } from './artisan/import.js';
4
+ export interface RoastProfile {
5
+ roast_id: number;
6
+ batch_name: string | null;
7
+ coffee_id: number | null;
8
+ coffee_name: string | null;
9
+ roast_date: string | null;
10
+ oz_in: number | null;
11
+ oz_out: number | null;
12
+ weight_loss_percent: number | null;
13
+ roast_notes: string | null;
14
+ user: string;
15
+ last_updated: string;
16
+ roaster_type: string | null;
17
+ roaster_size: string | null;
18
+ temperature_unit: string | null;
19
+ charge_time: number | null;
20
+ drop_time: number | null;
21
+ fc_start_time: number | null;
22
+ fc_end_time: number | null;
23
+ fc_start_temp: number | null;
24
+ drop_temp: number | null;
25
+ charge_temp: number | null;
26
+ dry_percent: number | null;
27
+ maillard_percent: number | null;
28
+ development_percent: number | null;
29
+ total_roast_time: number | null;
30
+ data_source: string | null;
31
+ roast_uuid: string | null;
32
+ temperatures?: TemperatureEntry[];
33
+ events?: RoastEventEntry[];
34
+ }
35
+ export interface TemperatureEntry {
36
+ roast_id: number;
37
+ time_seconds: number;
38
+ bean_temp: number | null;
39
+ environmental_temp: number | null;
40
+ }
41
+ export interface RoastEventEntry {
42
+ roast_id: number;
43
+ time_seconds: number;
44
+ event_type: number | null;
45
+ event_value: string | null;
46
+ }
47
+ export declare const listRoastsSchema: z.ZodObject<{
48
+ coffeeId: z.ZodOptional<z.ZodNumber>;
49
+ limit: z.ZodDefault<z.ZodNumber>;
50
+ }, z.core.$strip>;
51
+ export type ListRoastsInput = z.input<typeof listRoastsSchema>;
52
+ export declare const getRoastSchema: z.ZodObject<{
53
+ id: z.ZodNumber;
54
+ includeTemps: z.ZodOptional<z.ZodBoolean>;
55
+ includeEvents: z.ZodOptional<z.ZodBoolean>;
56
+ }, z.core.$strip>;
57
+ export type GetRoastInput = z.input<typeof getRoastSchema>;
58
+ export declare const createRoastSchema: z.ZodObject<{
59
+ coffeeId: z.ZodNumber;
60
+ batchName: z.ZodOptional<z.ZodString>;
61
+ ozIn: z.ZodOptional<z.ZodNumber>;
62
+ ozOut: z.ZodOptional<z.ZodNumber>;
63
+ roastDate: z.ZodOptional<z.ZodString>;
64
+ notes: z.ZodOptional<z.ZodString>;
65
+ }, z.core.$strip>;
66
+ export type CreateRoastInput = z.input<typeof createRoastSchema>;
67
+ export declare const deleteRoastSchema: z.ZodObject<{
68
+ id: z.ZodNumber;
69
+ }, z.core.$strip>;
70
+ export type DeleteRoastInput = z.input<typeof deleteRoastSchema>;
71
+ /**
72
+ * List roast profiles for a user.
73
+ */
74
+ export declare function listRoasts(supabase: SupabaseClient, userId: string, opts: ListRoastsInput): Promise<RoastProfile[]>;
75
+ /**
76
+ * Fetch a single roast profile by ID (must belong to userId).
77
+ * Optionally includes temperature curve and roast events.
78
+ */
79
+ export declare function getRoast(supabase: SupabaseClient, userId: string, id: number, opts?: {
80
+ includeTemps?: boolean;
81
+ includeEvents?: boolean;
82
+ }): Promise<RoastProfile>;
83
+ /**
84
+ * Create a new roast profile (coffeeId must be an inventory item belonging to userId).
85
+ */
86
+ export declare function createRoast(supabase: SupabaseClient, userId: string, input: CreateRoastInput): Promise<RoastProfile>;
87
+ /**
88
+ * Delete a roast profile (must belong to userId).
89
+ */
90
+ export declare function deleteRoast(supabase: SupabaseClient, userId: string, id: number): Promise<void>;
91
+ export declare const importRoastSchema: z.ZodObject<{
92
+ fileContent: z.ZodString;
93
+ fileName: z.ZodString;
94
+ coffeeId: z.ZodNumber;
95
+ batchName: z.ZodOptional<z.ZodString>;
96
+ ozIn: z.ZodOptional<z.ZodNumber>;
97
+ roastNotes: z.ZodOptional<z.ZodString>;
98
+ }, z.core.$strip>;
99
+ export type ImportRoastInput = z.input<typeof importRoastSchema>;
100
+ export interface ImportRoastResult extends ArtisanImportResult {
101
+ roast_id: number;
102
+ batch_name: string;
103
+ coffee_name: string;
104
+ coffee_id: number;
105
+ }
106
+ /**
107
+ * Extract the input weight in ounces from an Artisan .alog weight array.
108
+ * Falls back to undefined if weight data is absent or unparseable.
109
+ *
110
+ * @param weight - The `weight` field from ArtisanRoastData: [input, output, unit]
111
+ */
112
+ export declare function extractOzFromAlog(weight: [number, number, string] | undefined): number | undefined;
113
+ /**
114
+ * Generate a default batch name: "{coffee_name} {YYYY-MM-DD}".
115
+ */
116
+ export declare function defaultBatchName(coffeeName: string, dateIso: string): string;
117
+ /**
118
+ * Import a roast from an Artisan .alog file in one step:
119
+ * 1. Verify inventory ownership + get coffee name
120
+ * 2. Create a new roast_profile row
121
+ * 3. Run the full Artisan import (temperatures, events, profile metadata)
122
+ * 4. Return the combined result
123
+ *
124
+ * The supabase client must already be authenticated as userId.
125
+ */
126
+ export declare function importRoastFromFile(supabase: SupabaseClient, userId: string, input: ImportRoastInput): Promise<ImportRoastResult>;
127
+ //# sourceMappingURL=roast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roast.d.ts","sourceRoot":"","sources":["../../src/lib/roast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,284 @@
1
+ import { z } from 'zod';
2
+ import { AuthError, PrvrsError } from './errors.js';
3
+ import { importArtisanData } from './artisan/import.js';
4
+ // ─── Shared select columns ────────────────────────────────────────────────────
5
+ const ROAST_LIST_SELECT = 'roast_id, batch_name, coffee_id, coffee_name, roast_date, oz_in, oz_out, weight_loss_percent, roast_notes, roaster_type, roaster_size, temperature_unit, total_roast_time, development_percent, data_source, last_updated';
6
+ const ROAST_DETAIL_SELECT = 'roast_id, batch_name, coffee_id, coffee_name, roast_date, oz_in, oz_out, weight_loss_percent, roast_notes, roaster_type, total_roast_time, data_source, last_updated';
7
+ // ─── Zod schemas ──────────────────────────────────────────────────────────────
8
+ export const listRoastsSchema = z.object({
9
+ coffeeId: z.number().int().positive().optional(),
10
+ limit: z.number().int().min(1).default(20),
11
+ });
12
+ export const getRoastSchema = z.object({
13
+ id: z.number().int().positive(),
14
+ includeTemps: z.boolean().optional(),
15
+ includeEvents: z.boolean().optional(),
16
+ });
17
+ export const createRoastSchema = z.object({
18
+ coffeeId: z.number().int().positive(),
19
+ batchName: z.string().optional(),
20
+ ozIn: z.number().positive().optional(),
21
+ ozOut: z.number().positive().optional(),
22
+ roastDate: z.string().optional(),
23
+ notes: z.string().optional(),
24
+ });
25
+ export const deleteRoastSchema = z.object({
26
+ id: z.number().int().positive(),
27
+ });
28
+ // ─── Pure lib functions ───────────────────────────────────────────────────────
29
+ /**
30
+ * List roast profiles for a user.
31
+ */
32
+ export async function listRoasts(supabase, userId, opts) {
33
+ const parsed = listRoastsSchema.parse(opts);
34
+ let query = supabase.from('roast_profiles').select(ROAST_LIST_SELECT).eq('user', userId);
35
+ if (parsed.coffeeId !== undefined) {
36
+ query = query.eq('coffee_id', parsed.coffeeId);
37
+ }
38
+ const { data, error } = await query.order('roast_date', { ascending: false }).limit(parsed.limit);
39
+ if (error)
40
+ throw error;
41
+ return (data ?? []);
42
+ }
43
+ /**
44
+ * Fetch a single roast profile by ID (must belong to userId).
45
+ * Optionally includes temperature curve and roast events.
46
+ */
47
+ export async function getRoast(supabase, userId, id, opts = {}) {
48
+ getRoastSchema.parse({ id, ...opts });
49
+ const { data: profile, error: profileError } = await supabase
50
+ .from('roast_profiles')
51
+ .select('*')
52
+ .eq('roast_id', id)
53
+ .eq('user', userId)
54
+ .single();
55
+ if (profileError) {
56
+ if (profileError.code === 'PGRST116') {
57
+ throw new AuthError(`Roast profile ${id} not found or does not belong to you.`);
58
+ }
59
+ throw profileError;
60
+ }
61
+ const result = { ...profile };
62
+ if (opts.includeTemps) {
63
+ const { data: temps, error: tempError } = await supabase
64
+ .from('roast_temperatures')
65
+ .select('roast_id, time_seconds, bean_temp, environmental_temp')
66
+ .eq('roast_id', id)
67
+ .order('time_seconds', { ascending: true });
68
+ if (tempError)
69
+ throw tempError;
70
+ result.temperatures = (temps ?? []);
71
+ }
72
+ if (opts.includeEvents) {
73
+ const { data: events, error: eventsError } = await supabase
74
+ .from('roast_events')
75
+ .select('roast_id, time_seconds, event_type, event_value')
76
+ .eq('roast_id', id)
77
+ .order('time_seconds', { ascending: true });
78
+ if (eventsError)
79
+ throw eventsError;
80
+ result.events = (events ?? []);
81
+ }
82
+ return result;
83
+ }
84
+ /**
85
+ * Create a new roast profile (coffeeId must be an inventory item belonging to userId).
86
+ */
87
+ export async function createRoast(supabase, userId, input) {
88
+ const parsed = createRoastSchema.parse(input);
89
+ const todayIso = () => new Date().toISOString().slice(0, 10);
90
+ // Verify ownership of the inventory item and get coffee name for default batch name
91
+ const { data: invItem, error: invError } = await supabase
92
+ .from('green_coffee_inv')
93
+ .select('id, coffee_catalog!catalog_id (name)')
94
+ .eq('id', parsed.coffeeId)
95
+ .eq('user', userId)
96
+ .single();
97
+ if (invError || !invItem) {
98
+ throw new AuthError(`Inventory item ${parsed.coffeeId} not found or does not belong to you.`);
99
+ }
100
+ const roastDate = parsed.roastDate ?? todayIso();
101
+ let batchName = parsed.batchName;
102
+ if (!batchName) {
103
+ const catalogRaw = invItem.coffee_catalog;
104
+ const catalog = Array.isArray(catalogRaw) ? (catalogRaw[0] ?? null) : catalogRaw;
105
+ const coffeeName = catalog?.name ?? `Coffee #${parsed.coffeeId}`;
106
+ batchName = `${coffeeName} — ${roastDate}`;
107
+ }
108
+ const insertPayload = {
109
+ user: userId,
110
+ coffee_id: parsed.coffeeId,
111
+ batch_name: batchName,
112
+ roast_date: roastDate,
113
+ };
114
+ if (parsed.ozIn !== undefined)
115
+ insertPayload.oz_in = parsed.ozIn;
116
+ if (parsed.ozOut !== undefined)
117
+ insertPayload.oz_out = parsed.ozOut;
118
+ if (parsed.notes !== undefined)
119
+ insertPayload.roast_notes = parsed.notes;
120
+ const { data: inserted, error: insertError } = await supabase
121
+ .from('roast_profiles')
122
+ .insert(insertPayload)
123
+ .select('roast_id')
124
+ .single();
125
+ if (insertError)
126
+ throw insertError;
127
+ // Re-fetch the full row
128
+ const { data, error } = await supabase
129
+ .from('roast_profiles')
130
+ .select(ROAST_DETAIL_SELECT)
131
+ .eq('roast_id', inserted.roast_id)
132
+ .single();
133
+ if (error)
134
+ throw error;
135
+ return data;
136
+ }
137
+ /**
138
+ * Delete a roast profile (must belong to userId).
139
+ */
140
+ export async function deleteRoast(supabase, userId, id) {
141
+ deleteRoastSchema.parse({ id });
142
+ if (isNaN(id)) {
143
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid roast ID: "${id}".`);
144
+ }
145
+ // Verify ownership
146
+ const { data: existing, error: fetchError } = await supabase
147
+ .from('roast_profiles')
148
+ .select('roast_id')
149
+ .eq('roast_id', id)
150
+ .eq('user', userId)
151
+ .single();
152
+ if (fetchError || !existing) {
153
+ throw new AuthError(`Roast profile ${id} not found or does not belong to you.`);
154
+ }
155
+ const { error: deleteError } = await supabase
156
+ .from('roast_profiles')
157
+ .delete()
158
+ .eq('roast_id', id)
159
+ .eq('user', userId);
160
+ if (deleteError)
161
+ throw deleteError;
162
+ }
163
+ // ─── Roast import from .alog file ─────────────────────────────────────────────
164
+ export const importRoastSchema = z.object({
165
+ fileContent: z.string().min(1),
166
+ fileName: z.string().min(1),
167
+ coffeeId: z.number().int().positive(),
168
+ batchName: z.string().optional(),
169
+ ozIn: z.number().positive().optional(),
170
+ roastNotes: z.string().optional(),
171
+ });
172
+ /**
173
+ * Extract the input weight in ounces from an Artisan .alog weight array.
174
+ * Falls back to undefined if weight data is absent or unparseable.
175
+ *
176
+ * @param weight - The `weight` field from ArtisanRoastData: [input, output, unit]
177
+ */
178
+ export function extractOzFromAlog(weight) {
179
+ if (!weight || !Array.isArray(weight) || weight.length < 3)
180
+ return undefined;
181
+ const [inputWeight, , unit] = weight;
182
+ if (typeof inputWeight !== 'number' || inputWeight <= 0)
183
+ return undefined;
184
+ if (typeof unit !== 'string')
185
+ return undefined;
186
+ const unitLower = unit.toLowerCase();
187
+ if (unitLower === 'g' || unitLower === 'gr' || unitLower === 'gram' || unitLower === 'grams') {
188
+ return inputWeight / 28.3495;
189
+ }
190
+ if (unitLower === 'oz' || unitLower === 'ounce' || unitLower === 'ounces') {
191
+ return inputWeight;
192
+ }
193
+ if (unitLower === 'kg' || unitLower === 'kilogram' || unitLower === 'kilograms') {
194
+ return inputWeight * 35.274;
195
+ }
196
+ if (unitLower === 'lb' ||
197
+ unitLower === 'lbs' ||
198
+ unitLower === 'pound' ||
199
+ unitLower === 'pounds') {
200
+ return inputWeight * 16;
201
+ }
202
+ // Unknown unit — return undefined rather than a wrong number
203
+ return undefined;
204
+ }
205
+ /**
206
+ * Generate a default batch name: "{coffee_name} {YYYY-MM-DD}".
207
+ */
208
+ export function defaultBatchName(coffeeName, dateIso) {
209
+ return `${coffeeName} ${dateIso}`;
210
+ }
211
+ /**
212
+ * Import a roast from an Artisan .alog file in one step:
213
+ * 1. Verify inventory ownership + get coffee name
214
+ * 2. Create a new roast_profile row
215
+ * 3. Run the full Artisan import (temperatures, events, profile metadata)
216
+ * 4. Return the combined result
217
+ *
218
+ * The supabase client must already be authenticated as userId.
219
+ */
220
+ export async function importRoastFromFile(supabase, userId, input) {
221
+ const parsed = importRoastSchema.parse(input);
222
+ // 1. Verify ownership and get coffee name
223
+ const { data: invItem, error: invError } = await supabase
224
+ .from('green_coffee_inv')
225
+ .select('id, coffee_catalog!catalog_id (name)')
226
+ .eq('id', parsed.coffeeId)
227
+ .eq('user', userId)
228
+ .single();
229
+ if (invError || !invItem) {
230
+ throw new AuthError(`Inventory item ${parsed.coffeeId} not found or does not belong to you.`);
231
+ }
232
+ const catalogRaw = invItem.coffee_catalog;
233
+ const catalog = Array.isArray(catalogRaw) ? (catalogRaw[0] ?? null) : catalogRaw;
234
+ const coffeeName = catalog?.name ?? `Coffee #${parsed.coffeeId}`;
235
+ // 2. Extract roast date + weight from the .alog if not provided by caller
236
+ let artisanRaw;
237
+ try {
238
+ // Light pre-parse to grab roastdate and weight without a full import cycle.
239
+ // We import parseArtisanFile lazily here to keep imports tidy.
240
+ const { parseArtisanFile } = await import('./artisan/import.js');
241
+ const artisan = await parseArtisanFile(parsed.fileContent, parsed.fileName);
242
+ artisanRaw = artisan;
243
+ }
244
+ catch {
245
+ // Pre-parse failure is non-fatal here; the real import will surface the error
246
+ }
247
+ const roastDate = artisanRaw?.roastdate ?? new Date().toISOString().slice(0, 10);
248
+ let ozIn = parsed.ozIn;
249
+ if (ozIn === undefined && artisanRaw?.weight) {
250
+ ozIn = extractOzFromAlog(artisanRaw.weight);
251
+ }
252
+ const batchName = parsed.batchName ?? defaultBatchName(coffeeName, roastDate);
253
+ // 3. Create roast_profile row
254
+ const insertPayload = {
255
+ user: userId,
256
+ coffee_id: parsed.coffeeId,
257
+ coffee_name: coffeeName,
258
+ batch_name: batchName,
259
+ roast_date: roastDate,
260
+ data_source: 'artisan_import',
261
+ };
262
+ if (ozIn !== undefined)
263
+ insertPayload.oz_in = ozIn;
264
+ if (parsed.roastNotes !== undefined)
265
+ insertPayload.roast_notes = parsed.roastNotes;
266
+ const { data: inserted, error: insertError } = await supabase
267
+ .from('roast_profiles')
268
+ .insert(insertPayload)
269
+ .select('roast_id')
270
+ .single();
271
+ if (insertError)
272
+ throw insertError;
273
+ const roastId = inserted.roast_id;
274
+ // 4. Run the full Artisan import (updates profile metadata + writes temps/events)
275
+ const importResult = await importArtisanData(supabase, roastId, userId, parsed.fileContent, parsed.fileName);
276
+ return {
277
+ ...importResult,
278
+ roast_id: roastId,
279
+ batch_name: batchName,
280
+ coffee_name: coffeeName,
281
+ coffee_id: parsed.coffeeId,
282
+ };
283
+ }
284
+ //# sourceMappingURL=roast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roast.js","sourceRoot":"","sources":["../../src/lib/roast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAmDxD,iFAAiF;AAEjF,MAAM,iBAAiB,GACrB,2NAA2N,CAAC;AAE9N,MAAM,mBAAmB,GACvB,sKAAsK,CAAC;AAEzK,iFAAiF;AAEjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,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,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC/B,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACpC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACtC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAIH,iFAAiF;AAEjF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,QAAwB,EACxB,MAAc,EACd,IAAqB;IAErB,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE5C,IAAI,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEzF,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAElG,IAAI,KAAK;QAAE,MAAM,KAAK,CAAC;IAEvB,OAAO,CAAC,IAAI,IAAI,EAAE,CAAmB,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,QAAwB,EACxB,MAAc,EACd,EAAU,EACV,OAA4D,EAAE;IAE9D,cAAc,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAEtC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,QAAQ;SAC1D,IAAI,CAAC,gBAAgB,CAAC;SACtB,MAAM,CAAC,GAAG,CAAC;SACX,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC;SAClB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,YAAY,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACrC,MAAM,IAAI,SAAS,CAAC,iBAAiB,EAAE,uCAAuC,CAAC,CAAC;QAClF,CAAC;QACD,MAAM,YAAY,CAAC;IACrB,CAAC;IAED,MAAM,MAAM,GAAiB,EAAE,GAAG,OAAO,EAAE,CAAC;IAE5C,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,QAAQ;aACrD,IAAI,CAAC,oBAAoB,CAAC;aAC1B,MAAM,CAAC,uDAAuD,CAAC;aAC/D,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC;aAClB,KAAK,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9C,IAAI,SAAS;YAAE,MAAM,SAAS,CAAC;QAC/B,MAAM,CAAC,YAAY,GAAG,CAAC,KAAK,IAAI,EAAE,CAAuB,CAAC;IAC5D,CAAC;IAED,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ;aACxD,IAAI,CAAC,cAAc,CAAC;aACpB,MAAM,CAAC,iDAAiD,CAAC;aACzD,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC;aAClB,KAAK,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9C,IAAI,WAAW;YAAE,MAAM,WAAW,CAAC;QACnC,MAAM,CAAC,MAAM,GAAG,CAAC,MAAM,IAAI,EAAE,CAAsB,CAAC;IACtD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAwB,EACxB,MAAc,EACd,KAAuB;IAEvB,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAE7D,oFAAoF;IACpF,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ;SACtD,IAAI,CAAC,kBAAkB,CAAC;SACxB,MAAM,CAAC,sCAAsC,CAAC;SAC9C,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC;SACzB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,SAAS,CAAC,kBAAkB,MAAM,CAAC,QAAQ,uCAAuC,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,QAAQ,EAAE,CAAC;IAEjD,IAAI,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IACjC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,UAAU,GAAG,OAAO,CAAC,cAGnB,CAAC;QACT,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QACjF,MAAM,UAAU,GAAG,OAAO,EAAE,IAAI,IAAI,WAAW,MAAM,CAAC,QAAQ,EAAE,CAAC;QACjE,SAAS,GAAG,GAAG,UAAU,MAAM,SAAS,EAAE,CAAC;IAC7C,CAAC;IAED,MAAM,aAAa,GAA4B;QAC7C,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,MAAM,CAAC,QAAQ;QAC1B,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,SAAS;KACtB,CAAC;IAEF,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,aAAa,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC;IACjE,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;QAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC;IACpE,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;QAAE,aAAa,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;IAEzE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ;SAC1D,IAAI,CAAC,gBAAgB,CAAC;SACtB,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,UAAU,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,WAAW;QAAE,MAAM,WAAW,CAAC;IAEnC,wBAAwB;IACxB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ;SACnC,IAAI,CAAC,gBAAgB,CAAC;SACtB,MAAM,CAAC,mBAAmB,CAAC;SAC3B,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC;SACjC,MAAM,EAAE,CAAC;IAEZ,IAAI,KAAK;QAAE,MAAM,KAAK,CAAC;IAEvB,OAAO,IAAoB,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAwB,EACxB,MAAc,EACd,EAAU;IAEV,iBAAiB,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAEhC,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACd,MAAM,IAAI,UAAU,CAAC,kBAAkB,EAAE,sBAAsB,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;IAED,mBAAmB;IACnB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,QAAQ;SACzD,IAAI,CAAC,gBAAgB,CAAC;SACtB,MAAM,CAAC,UAAU,CAAC;SAClB,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC;SAClB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,SAAS,CAAC,iBAAiB,EAAE,uCAAuC,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ;SAC1C,IAAI,CAAC,gBAAgB,CAAC;SACtB,MAAM,EAAE;SACR,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC;SAClB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtB,IAAI,WAAW;QAAE,MAAM,WAAW,CAAC;AACrC,CAAC;AAED,iFAAiF;AAEjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAWH;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA4C;IAE5C,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7E,MAAM,CAAC,WAAW,EAAE,AAAD,EAAG,IAAI,CAAC,GAAG,MAAM,CAAC;IACrC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1E,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAE/C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;QAC7F,OAAO,WAAW,GAAG,OAAO,CAAC;IAC/B,CAAC;IACD,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,OAAO,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC1E,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,UAAU,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;QAChF,OAAO,WAAW,GAAG,MAAM,CAAC;IAC9B,CAAC;IACD,IACE,SAAS,KAAK,IAAI;QAClB,SAAS,KAAK,KAAK;QACnB,SAAS,KAAK,OAAO;QACrB,SAAS,KAAK,QAAQ,EACtB,CAAC;QACD,OAAO,WAAW,GAAG,EAAE,CAAC;IAC1B,CAAC;IACD,6DAA6D;IAC7D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,OAAe;IAClE,OAAO,GAAG,UAAU,IAAI,OAAO,EAAE,CAAC;AACpC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,QAAwB,EACxB,MAAc,EACd,KAAuB;IAEvB,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE9C,0CAA0C;IAC1C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ;SACtD,IAAI,CAAC,kBAAkB,CAAC;SACxB,MAAM,CAAC,sCAAsC,CAAC;SAC9C,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC;SACzB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,SAAS,CAAC,kBAAkB,MAAM,CAAC,QAAQ,uCAAuC,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,cAGnB,CAAC;IACT,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACjF,MAAM,UAAU,GAAG,OAAO,EAAE,IAAI,IAAI,WAAW,MAAM,CAAC,QAAQ,EAAE,CAAC;IAEjE,0EAA0E;IAC1E,IAAI,UAA+C,CAAC;IACpD,IAAI,CAAC;QACH,4EAA4E;QAC5E,+DAA+D;QAC/D,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACjE,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5E,UAAU,GAAG,OAA6C,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,8EAA8E;IAChF,CAAC;IAED,MAAM,SAAS,GACZ,UAAU,EAAE,SAAgC,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEzF,IAAI,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACvB,IAAI,IAAI,KAAK,SAAS,IAAI,UAAU,EAAE,MAAM,EAAE,CAAC;QAC7C,IAAI,GAAG,iBAAiB,CAAC,UAAU,CAAC,MAAkC,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,gBAAgB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAE9E,8BAA8B;IAC9B,MAAM,aAAa,GAA4B;QAC7C,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,MAAM,CAAC,QAAQ;QAC1B,WAAW,EAAE,UAAU;QACvB,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,SAAS;QACrB,WAAW,EAAE,gBAAgB;KAC9B,CAAC;IACF,IAAI,IAAI,KAAK,SAAS;QAAE,aAAa,CAAC,KAAK,GAAG,IAAI,CAAC;IACnD,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS;QAAE,aAAa,CAAC,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC;IAEnF,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,QAAQ;SAC1D,IAAI,CAAC,gBAAgB,CAAC;SACtB,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,UAAU,CAAC;SAClB,MAAM,EAAE,CAAC;IAEZ,IAAI,WAAW;QAAE,MAAM,WAAW,CAAC;IAEnC,MAAM,OAAO,GAAY,QAAiC,CAAC,QAAQ,CAAC;IAEpE,kFAAkF;IAClF,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAC1C,QAAQ,EACR,OAAO,EACP,MAAM,EACN,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,QAAQ,CAChB,CAAC;IAEF,OAAO;QACL,GAAG,YAAY;QACf,QAAQ,EAAE,OAAO;QACjB,UAAU,EAAE,SAAS;QACrB,WAAW,EAAE,UAAU;QACvB,SAAS,EAAE,MAAM,CAAC,QAAQ;KAC3B,CAAC;AACJ,CAAC"}
@@ -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":"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"}