@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/lib/supabase.js
CHANGED
|
@@ -28,7 +28,7 @@ export function createAnonClient() {
|
|
|
28
28
|
export async function createAuthenticatedClient() {
|
|
29
29
|
const creds = await readCredentials();
|
|
30
30
|
if (!creds) {
|
|
31
|
-
throw new AuthError('Not logged in. Run `
|
|
31
|
+
throw new AuthError('Not logged in. Run `purvey auth login` first.');
|
|
32
32
|
}
|
|
33
33
|
const client = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
34
34
|
auth: {
|
|
@@ -52,7 +52,7 @@ export async function createAuthenticatedClient() {
|
|
|
52
52
|
if (error) {
|
|
53
53
|
// Refresh failed — credentials are expired
|
|
54
54
|
await deleteCredentials();
|
|
55
|
-
throw new AuthError('Session expired. Run `
|
|
55
|
+
throw new AuthError('Session expired. Run `purvey auth login` to re-authenticate.');
|
|
56
56
|
}
|
|
57
57
|
return client;
|
|
58
58
|
}
|
package/dist/lib/supabase.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../src/lib/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;;GAIG;AACH,MAAM,YAAY,GAChB,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,0CAA0C,CAAC;AACnF,MAAM,iBAAiB,GACrB,OAAO,CAAC,GAAG,CAAC,2BAA2B;IACvC,kNAAkN,CAAC;AAErN;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,YAAY,CAAC,YAAa,EAAE,iBAAkB,EAAE;QACrD,IAAI,EAAE;YACJ,cAAc,EAAE,KAAK;YACrB,gBAAgB,EAAE,KAAK;SACxB;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB;IAC7C,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAC;IAEtC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../src/lib/supabase.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,10 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purveyors/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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
|
+
},
|
|
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"
|
|
8
18
|
},
|
|
9
19
|
"files": [
|
|
10
20
|
"dist",
|
|
@@ -26,6 +36,15 @@
|
|
|
26
36
|
],
|
|
27
37
|
"author": "Reed Whetstone",
|
|
28
38
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc",
|
|
41
|
+
"dev": "tsx src/index.ts",
|
|
42
|
+
"lint": "prettier --check . && eslint .",
|
|
43
|
+
"format": "prettier --write .",
|
|
44
|
+
"check": "tsc --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest"
|
|
47
|
+
},
|
|
29
48
|
"engines": {
|
|
30
49
|
"node": ">=20"
|
|
31
50
|
},
|
|
@@ -33,7 +52,8 @@
|
|
|
33
52
|
"@supabase/supabase-js": "^2.49.1",
|
|
34
53
|
"chalk": "^5.4.1",
|
|
35
54
|
"commander": "^13.1.0",
|
|
36
|
-
"ora": "^8.2.0"
|
|
55
|
+
"ora": "^8.2.0",
|
|
56
|
+
"zod": "^4.3.6"
|
|
37
57
|
},
|
|
38
58
|
"devDependencies": {
|
|
39
59
|
"@eslint/eslintrc": "^3.3.0",
|
|
@@ -45,14 +65,5 @@
|
|
|
45
65
|
"tsx": "^4.19.3",
|
|
46
66
|
"typescript": "^5.8.2",
|
|
47
67
|
"vitest": "^3.0.9"
|
|
48
|
-
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"build": "tsc",
|
|
51
|
-
"dev": "tsx src/index.ts",
|
|
52
|
-
"lint": "prettier --check . && eslint .",
|
|
53
|
-
"format": "prettier --write .",
|
|
54
|
-
"check": "tsc --noEmit",
|
|
55
|
-
"test": "vitest run",
|
|
56
|
-
"test:watch": "vitest"
|
|
57
68
|
}
|
|
58
|
-
}
|
|
69
|
+
}
|