optimal-cli 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/optimal.d.ts +2 -0
- package/dist/bin/optimal.js +1590 -0
- package/dist/lib/assets/index.d.ts +79 -0
- package/dist/lib/assets/index.js +153 -0
- package/dist/lib/assets.d.ts +20 -0
- package/dist/lib/assets.js +112 -0
- package/dist/lib/auth/index.d.ts +83 -0
- package/dist/lib/auth/index.js +146 -0
- package/dist/lib/board/index.d.ts +39 -0
- package/dist/lib/board/index.js +285 -0
- package/dist/lib/board/types.d.ts +111 -0
- package/dist/lib/board/types.js +1 -0
- package/dist/lib/bot/claim.d.ts +3 -0
- package/dist/lib/bot/claim.js +20 -0
- package/dist/lib/bot/coordinator.d.ts +27 -0
- package/dist/lib/bot/coordinator.js +178 -0
- package/dist/lib/bot/heartbeat.d.ts +6 -0
- package/dist/lib/bot/heartbeat.js +30 -0
- package/dist/lib/bot/index.d.ts +9 -0
- package/dist/lib/bot/index.js +6 -0
- package/dist/lib/bot/protocol.d.ts +12 -0
- package/dist/lib/bot/protocol.js +74 -0
- package/dist/lib/bot/reporter.d.ts +3 -0
- package/dist/lib/bot/reporter.js +27 -0
- package/dist/lib/bot/skills.d.ts +26 -0
- package/dist/lib/bot/skills.js +69 -0
- package/dist/lib/budget/projections.d.ts +115 -0
- package/dist/lib/budget/projections.js +384 -0
- package/dist/lib/budget/scenarios.d.ts +93 -0
- package/dist/lib/budget/scenarios.js +214 -0
- package/dist/lib/cms/publish-blog.d.ts +62 -0
- package/dist/lib/cms/publish-blog.js +74 -0
- package/dist/lib/cms/strapi-client.d.ts +123 -0
- package/dist/lib/cms/strapi-client.js +213 -0
- package/dist/lib/config/registry.d.ts +17 -0
- package/dist/lib/config/registry.js +182 -0
- package/dist/lib/config/schema.d.ts +31 -0
- package/dist/lib/config/schema.js +25 -0
- package/dist/lib/config.d.ts +55 -0
- package/dist/lib/config.js +206 -0
- package/dist/lib/errors.d.ts +25 -0
- package/dist/lib/errors.js +91 -0
- package/dist/lib/format.d.ts +28 -0
- package/dist/lib/format.js +98 -0
- package/dist/lib/infra/deploy.d.ts +29 -0
- package/dist/lib/infra/deploy.js +58 -0
- package/dist/lib/infra/migrate.d.ts +34 -0
- package/dist/lib/infra/migrate.js +103 -0
- package/dist/lib/newsletter/distribute.d.ts +52 -0
- package/dist/lib/newsletter/distribute.js +193 -0
- package/{lib/newsletter/generate-insurance.ts → dist/lib/newsletter/generate-insurance.d.ts} +7 -24
- package/dist/lib/newsletter/generate-insurance.js +36 -0
- package/dist/lib/newsletter/generate.d.ts +104 -0
- package/dist/lib/newsletter/generate.js +571 -0
- package/dist/lib/returnpro/anomalies.d.ts +64 -0
- package/dist/lib/returnpro/anomalies.js +166 -0
- package/dist/lib/returnpro/audit.d.ts +32 -0
- package/dist/lib/returnpro/audit.js +147 -0
- package/dist/lib/returnpro/diagnose.d.ts +52 -0
- package/dist/lib/returnpro/diagnose.js +281 -0
- package/dist/lib/returnpro/kpis.d.ts +32 -0
- package/dist/lib/returnpro/kpis.js +192 -0
- package/dist/lib/returnpro/templates.d.ts +48 -0
- package/dist/lib/returnpro/templates.js +229 -0
- package/dist/lib/returnpro/upload-income.d.ts +25 -0
- package/dist/lib/returnpro/upload-income.js +235 -0
- package/dist/lib/returnpro/upload-netsuite.d.ts +37 -0
- package/dist/lib/returnpro/upload-netsuite.js +566 -0
- package/dist/lib/returnpro/upload-r1.d.ts +48 -0
- package/dist/lib/returnpro/upload-r1.js +398 -0
- package/dist/lib/returnpro/validate.d.ts +37 -0
- package/dist/lib/returnpro/validate.js +124 -0
- package/dist/lib/social/meta.d.ts +90 -0
- package/dist/lib/social/meta.js +160 -0
- package/dist/lib/social/post-generator.d.ts +83 -0
- package/dist/lib/social/post-generator.js +333 -0
- package/dist/lib/social/publish.d.ts +66 -0
- package/dist/lib/social/publish.js +226 -0
- package/dist/lib/social/scraper.d.ts +67 -0
- package/dist/lib/social/scraper.js +361 -0
- package/dist/lib/supabase.d.ts +4 -0
- package/dist/lib/supabase.js +20 -0
- package/dist/lib/transactions/delete-batch.d.ts +60 -0
- package/dist/lib/transactions/delete-batch.js +203 -0
- package/dist/lib/transactions/ingest.d.ts +43 -0
- package/dist/lib/transactions/ingest.js +555 -0
- package/dist/lib/transactions/stamp.d.ts +51 -0
- package/dist/lib/transactions/stamp.js +524 -0
- package/package.json +3 -4
- package/bin/optimal.ts +0 -1731
- package/lib/assets/index.ts +0 -225
- package/lib/assets.ts +0 -124
- package/lib/auth/index.ts +0 -189
- package/lib/board/index.ts +0 -309
- package/lib/board/types.ts +0 -124
- package/lib/bot/claim.ts +0 -43
- package/lib/bot/coordinator.ts +0 -254
- package/lib/bot/heartbeat.ts +0 -37
- package/lib/bot/index.ts +0 -9
- package/lib/bot/protocol.ts +0 -99
- package/lib/bot/reporter.ts +0 -42
- package/lib/bot/skills.ts +0 -81
- package/lib/budget/projections.ts +0 -561
- package/lib/budget/scenarios.ts +0 -312
- package/lib/cms/publish-blog.ts +0 -129
- package/lib/cms/strapi-client.ts +0 -302
- package/lib/config/registry.ts +0 -228
- package/lib/config/schema.ts +0 -58
- package/lib/config.ts +0 -247
- package/lib/errors.ts +0 -129
- package/lib/format.ts +0 -120
- package/lib/infra/.gitkeep +0 -0
- package/lib/infra/deploy.ts +0 -70
- package/lib/infra/migrate.ts +0 -141
- package/lib/newsletter/.gitkeep +0 -0
- package/lib/newsletter/distribute.ts +0 -256
- package/lib/newsletter/generate.ts +0 -735
- package/lib/returnpro/.gitkeep +0 -0
- package/lib/returnpro/anomalies.ts +0 -258
- package/lib/returnpro/audit.ts +0 -194
- package/lib/returnpro/diagnose.ts +0 -400
- package/lib/returnpro/kpis.ts +0 -255
- package/lib/returnpro/templates.ts +0 -323
- package/lib/returnpro/upload-income.ts +0 -311
- package/lib/returnpro/upload-netsuite.ts +0 -696
- package/lib/returnpro/upload-r1.ts +0 -563
- package/lib/returnpro/validate.ts +0 -154
- package/lib/social/meta.ts +0 -228
- package/lib/social/post-generator.ts +0 -468
- package/lib/social/publish.ts +0 -301
- package/lib/social/scraper.ts +0 -503
- package/lib/supabase.ts +0 -25
- package/lib/transactions/delete-batch.ts +0 -258
- package/lib/transactions/ingest.ts +0 -659
- package/lib/transactions/stamp.ts +0 -654
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transaction & Staging Batch Deletion — Safe Preview and Execute
|
|
3
|
-
*
|
|
4
|
-
* Provides safe batch deletion of transactions (OptimalOS) and staging
|
|
5
|
-
* financials (ReturnPro) with preview mode defaulting to dryRun=true.
|
|
6
|
-
*
|
|
7
|
-
* Tables:
|
|
8
|
-
* - transactions → OptimalOS Supabase (getSupabase('optimal'))
|
|
9
|
-
* - stg_financials_raw → ReturnPro Supabase (getSupabase('returnpro'))
|
|
10
|
-
*
|
|
11
|
-
* Columns:
|
|
12
|
-
* transactions: id, user_id, date, description, amount, category, source, stamp_match_type, created_at
|
|
13
|
-
* stg_financials_raw: id, account_code, account_name, amount (TEXT), month (YYYY-MM), source, user_id, created_at
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import 'dotenv/config'
|
|
17
|
-
import { getSupabase } from '../supabase.js'
|
|
18
|
-
import type { SupabaseClient } from '@supabase/supabase-js'
|
|
19
|
-
|
|
20
|
-
// =============================================================================
|
|
21
|
-
// TYPES
|
|
22
|
-
// =============================================================================
|
|
23
|
-
|
|
24
|
-
export interface DeleteBatchOptions {
|
|
25
|
-
table: 'transactions' | 'stg_financials_raw'
|
|
26
|
-
userId?: string // required for transactions
|
|
27
|
-
filters: {
|
|
28
|
-
dateFrom?: string // YYYY-MM-DD (maps to `date` on transactions, derived from `month` on staging)
|
|
29
|
-
dateTo?: string // YYYY-MM-DD
|
|
30
|
-
source?: string // e.g. 'Chase', 'Discover'
|
|
31
|
-
category?: string // transaction category
|
|
32
|
-
accountCode?: string // for stg_financials_raw
|
|
33
|
-
month?: string // YYYY-MM for stg_financials_raw
|
|
34
|
-
}
|
|
35
|
-
dryRun?: boolean // default true — must explicitly set false to delete
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface DeleteBatchResult {
|
|
39
|
-
table: string
|
|
40
|
-
deletedCount: number
|
|
41
|
-
dryRun: boolean
|
|
42
|
-
filters: Record<string, string>
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface PreviewResult {
|
|
46
|
-
table: string
|
|
47
|
-
matchCount: number
|
|
48
|
-
sample: Array<Record<string, unknown>>
|
|
49
|
-
groupedCounts: Record<string, number>
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// =============================================================================
|
|
53
|
-
// INTERNAL HELPERS
|
|
54
|
-
// =============================================================================
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Return the correct Supabase client for the given table.
|
|
58
|
-
*/
|
|
59
|
-
function getClientForTable(table: DeleteBatchOptions['table']): SupabaseClient {
|
|
60
|
-
return table === 'transactions'
|
|
61
|
-
? getSupabase('optimal')
|
|
62
|
-
: getSupabase('returnpro')
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Apply the shared set of filters to a Supabase query builder.
|
|
67
|
-
* Works for both SELECT and DELETE queries because both are PostgREST filters.
|
|
68
|
-
*
|
|
69
|
-
* For `stg_financials_raw`:
|
|
70
|
-
* - dateFrom / dateTo are ignored (use `month` instead)
|
|
71
|
-
* - month is applied as an eq filter on the `month` column
|
|
72
|
-
* - accountCode is applied as an eq filter on `account_code`
|
|
73
|
-
*
|
|
74
|
-
* For `transactions`:
|
|
75
|
-
* - dateFrom / dateTo are applied as gte/lte on `date`
|
|
76
|
-
* - source is applied as an eq filter on `source`
|
|
77
|
-
* - category is applied as an eq filter on `category`
|
|
78
|
-
* - userId is applied as an eq filter on `user_id`
|
|
79
|
-
*/
|
|
80
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
81
|
-
function applyFilters<T extends Record<string, any>>(
|
|
82
|
-
query: T,
|
|
83
|
-
table: DeleteBatchOptions['table'],
|
|
84
|
-
userId: string | undefined,
|
|
85
|
-
filters: DeleteBatchOptions['filters'],
|
|
86
|
-
): T {
|
|
87
|
-
let q = query
|
|
88
|
-
|
|
89
|
-
if (table === 'transactions') {
|
|
90
|
-
if (userId) q = (q as unknown as { eq(col: string, val: string): T }).eq('user_id', userId) as unknown as T
|
|
91
|
-
if (filters.dateFrom) q = (q as unknown as { gte(col: string, val: string): T }).gte('date', filters.dateFrom) as unknown as T
|
|
92
|
-
if (filters.dateTo) q = (q as unknown as { lte(col: string, val: string): T }).lte('date', filters.dateTo) as unknown as T
|
|
93
|
-
if (filters.source) q = (q as unknown as { eq(col: string, val: string): T }).eq('source', filters.source) as unknown as T
|
|
94
|
-
if (filters.category) q = (q as unknown as { eq(col: string, val: string): T }).eq('category', filters.category) as unknown as T
|
|
95
|
-
} else {
|
|
96
|
-
// stg_financials_raw
|
|
97
|
-
if (userId) q = (q as unknown as { eq(col: string, val: string): T }).eq('user_id', userId) as unknown as T
|
|
98
|
-
if (filters.month) q = (q as unknown as { eq(col: string, val: string): T }).eq('month', filters.month) as unknown as T
|
|
99
|
-
if (filters.accountCode) q = (q as unknown as { eq(col: string, val: string): T }).eq('account_code', filters.accountCode) as unknown as T
|
|
100
|
-
if (filters.source) q = (q as unknown as { eq(col: string, val: string): T }).eq('source', filters.source) as unknown as T
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return q
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Serialize active filters for the result record (human-readable).
|
|
108
|
-
*/
|
|
109
|
-
function serializeFilters(
|
|
110
|
-
table: DeleteBatchOptions['table'],
|
|
111
|
-
userId: string | undefined,
|
|
112
|
-
filters: DeleteBatchOptions['filters'],
|
|
113
|
-
): Record<string, string> {
|
|
114
|
-
const out: Record<string, string> = {}
|
|
115
|
-
if (userId) out.user_id = userId
|
|
116
|
-
if (table === 'transactions') {
|
|
117
|
-
if (filters.dateFrom) out.dateFrom = filters.dateFrom
|
|
118
|
-
if (filters.dateTo) out.dateTo = filters.dateTo
|
|
119
|
-
if (filters.source) out.source = filters.source
|
|
120
|
-
if (filters.category) out.category = filters.category
|
|
121
|
-
} else {
|
|
122
|
-
if (filters.month) out.month = filters.month
|
|
123
|
-
if (filters.accountCode) out.accountCode = filters.accountCode
|
|
124
|
-
if (filters.source) out.source = filters.source
|
|
125
|
-
}
|
|
126
|
-
return out
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// =============================================================================
|
|
130
|
-
// PUBLIC FUNCTIONS
|
|
131
|
-
// =============================================================================
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Preview what would be deleted without touching any data.
|
|
135
|
-
*
|
|
136
|
-
* Returns:
|
|
137
|
-
* - matchCount: total rows matching the filters
|
|
138
|
-
* - sample: first 10 matching rows
|
|
139
|
-
* - groupedCounts: row counts grouped by `source` (transactions) or `account_code` (staging)
|
|
140
|
-
*/
|
|
141
|
-
export async function previewBatch(opts: DeleteBatchOptions): Promise<PreviewResult> {
|
|
142
|
-
const { table, userId, filters } = opts
|
|
143
|
-
const supabase = getClientForTable(table)
|
|
144
|
-
|
|
145
|
-
// --- Count matching rows ---
|
|
146
|
-
const countQuery = supabase
|
|
147
|
-
.from(table)
|
|
148
|
-
.select('*', { count: 'exact', head: true })
|
|
149
|
-
|
|
150
|
-
const countQueryWithFilters = applyFilters(countQuery, table, userId, filters)
|
|
151
|
-
const { count, error: countError } = await countQueryWithFilters
|
|
152
|
-
|
|
153
|
-
if (countError) {
|
|
154
|
-
throw new Error(`previewBatch count error on ${table}: ${countError.message}`)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const matchCount = count ?? 0
|
|
158
|
-
|
|
159
|
-
// --- Fetch sample rows (first 10) ---
|
|
160
|
-
const sampleQuery = supabase
|
|
161
|
-
.from(table)
|
|
162
|
-
.select('*')
|
|
163
|
-
.limit(10)
|
|
164
|
-
|
|
165
|
-
const sampleQueryWithFilters = applyFilters(sampleQuery, table, userId, filters)
|
|
166
|
-
const { data: sampleData, error: sampleError } = await sampleQueryWithFilters
|
|
167
|
-
|
|
168
|
-
if (sampleError) {
|
|
169
|
-
throw new Error(`previewBatch sample error on ${table}: ${sampleError.message}`)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const sample = (sampleData ?? []) as Array<Record<string, unknown>>
|
|
173
|
-
|
|
174
|
-
// --- Grouped counts ---
|
|
175
|
-
// Group by `source` for transactions, `account_code` for staging
|
|
176
|
-
const groupCol = table === 'transactions' ? 'source' : 'account_code'
|
|
177
|
-
|
|
178
|
-
const groupQuery = supabase
|
|
179
|
-
.from(table)
|
|
180
|
-
.select(groupCol)
|
|
181
|
-
|
|
182
|
-
const groupQueryWithFilters = applyFilters(groupQuery, table, userId, filters)
|
|
183
|
-
const { data: groupData, error: groupError } = await groupQueryWithFilters
|
|
184
|
-
|
|
185
|
-
if (groupError) {
|
|
186
|
-
throw new Error(`previewBatch group error on ${table}: ${groupError.message}`)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const groupedCounts: Record<string, number> = {}
|
|
190
|
-
for (const row of (groupData ?? []) as Array<Record<string, unknown>>) {
|
|
191
|
-
const key = (row[groupCol] as string | null | undefined) ?? '(unknown)'
|
|
192
|
-
groupedCounts[key] = (groupedCounts[key] ?? 0) + 1
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
table,
|
|
197
|
-
matchCount,
|
|
198
|
-
sample,
|
|
199
|
-
groupedCounts,
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Delete matching rows in batch — or preview them without deleting (dryRun=true).
|
|
205
|
-
*
|
|
206
|
-
* Safety: dryRun defaults to TRUE. Caller must explicitly pass dryRun=false
|
|
207
|
-
* to execute an actual deletion.
|
|
208
|
-
*
|
|
209
|
-
* In dryRun mode: counts matching rows and returns deletedCount=0.
|
|
210
|
-
* In execute mode: issues a Supabase DELETE with the same filters and returns
|
|
211
|
-
* the number of rows deleted.
|
|
212
|
-
*/
|
|
213
|
-
export async function deleteBatch(opts: DeleteBatchOptions): Promise<DeleteBatchResult> {
|
|
214
|
-
const { table, userId, filters } = opts
|
|
215
|
-
const dryRun = opts.dryRun ?? true // safe by default
|
|
216
|
-
const supabase = getClientForTable(table)
|
|
217
|
-
const serializedFilters = serializeFilters(table, userId, filters)
|
|
218
|
-
|
|
219
|
-
if (dryRun) {
|
|
220
|
-
// Count matching rows without deleting
|
|
221
|
-
const countQuery = supabase
|
|
222
|
-
.from(table)
|
|
223
|
-
.select('*', { count: 'exact', head: true })
|
|
224
|
-
|
|
225
|
-
const countQueryWithFilters = applyFilters(countQuery, table, userId, filters)
|
|
226
|
-
const { count, error } = await countQueryWithFilters
|
|
227
|
-
|
|
228
|
-
if (error) {
|
|
229
|
-
throw new Error(`deleteBatch dry-run count error on ${table}: ${error.message}`)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
table,
|
|
234
|
-
deletedCount: 0,
|
|
235
|
-
dryRun: true,
|
|
236
|
-
filters: serializedFilters,
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Execute deletion
|
|
241
|
-
const deleteQuery = supabase
|
|
242
|
-
.from(table)
|
|
243
|
-
.delete({ count: 'exact' })
|
|
244
|
-
|
|
245
|
-
const deleteQueryWithFilters = applyFilters(deleteQuery, table, userId, filters)
|
|
246
|
-
const { count, error } = await deleteQueryWithFilters
|
|
247
|
-
|
|
248
|
-
if (error) {
|
|
249
|
-
throw new Error(`deleteBatch execute error on ${table}: ${error.message}`)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return {
|
|
253
|
-
table,
|
|
254
|
-
deletedCount: count ?? 0,
|
|
255
|
-
dryRun: false,
|
|
256
|
-
filters: serializedFilters,
|
|
257
|
-
}
|
|
258
|
-
}
|