@mxpicture/tesla-superchargers-cloud-store 0.1.4 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mxpicture/tesla-superchargers-cloud-store",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "description": "Cloud store intergration e.g. dropbox",
5
5
  "type": "module",
6
6
  "author": "MXPicture",
@@ -40,8 +40,8 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@mxpicture/tesla-superchargers-generated": "workspace:*",
44
- "@mxpicture/tesla-superchargers-mate": "workspace:*",
43
+ "@mxpicture/tesla-superchargers-generated": "^0.1.7",
44
+ "@mxpicture/tesla-superchargers-mate": "^0.1.7",
45
45
  "csv-parse": "^7.0.1",
46
46
  "dropbox": "^10.37.1",
47
47
  "json5": "^2.2.3",
@@ -1,32 +0,0 @@
1
- /**
2
- * Read-only Dropbox access: list a directory and download file content.
3
- *
4
- * Auth (read from the process environment on first use):
5
- * - `DROPBOX_KEY` + `DROPBOX_SECRET` + `DROPBOX_REFRESH_TOKEN` — long-lived;
6
- * the SDK exchanges the refresh token for short-lived access tokens itself.
7
- * - `DROPBOX_ACCESS_TOKEN` — fallback, e.g. a token generated in the Dropbox
8
- * App Console (expires after ~4h).
9
- */
10
- import { Dropbox, type DropboxOptions } from "dropbox";
11
- import type { DropboxAuthAccessParams, DropboxAuthClientParams } from "../types/dropbox.types.js";
12
- export declare class DropboxAuthFactory {
13
- private constructor();
14
- static byEnv(env: Record<string, string | undefined>): IDropboxAuth;
15
- }
16
- export declare abstract class IDropboxAuth {
17
- protected dbx: Dropbox | null;
18
- constructor();
19
- protected abstract clientParams(): DropboxOptions;
20
- client(): Dropbox;
21
- }
22
- export declare class DropboxAuthAccess extends IDropboxAuth {
23
- protected readonly auth: DropboxAuthAccessParams;
24
- constructor(auth: DropboxAuthAccessParams);
25
- protected clientParams(): DropboxOptions;
26
- }
27
- export declare class DropboxAuthClient extends IDropboxAuth {
28
- protected readonly auth: DropboxAuthClientParams;
29
- constructor(auth: DropboxAuthClientParams);
30
- protected clientParams(): DropboxOptions;
31
- }
32
- //# sourceMappingURL=DropboxAuth.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DropboxAuth.d.ts","sourceRoot":"","sources":["../../src/client/DropboxAuth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,qBAAa,kBAAkB;IAC7B,OAAO;WAEO,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,YAAY;CAsB3E;AAED,8BAAsB,YAAY;IAChC,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAQ;;IAIrC,SAAS,CAAC,QAAQ,CAAC,YAAY,IAAI,cAAc;IAE1C,MAAM,IAAI,OAAO;CAGzB;AAED,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;gBAE9B,IAAI,EAAE,uBAAuB;cAK7B,YAAY,IAAI,cAAc;CAGlD;AAED,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;gBAE9B,IAAI,EAAE,uBAAuB;IAKhD,SAAS,CAAC,YAAY,IAAI,cAAc;CAOzC"}
@@ -1,57 +0,0 @@
1
- /**
2
- * Read-only Dropbox access: list a directory and download file content.
3
- *
4
- * Auth (read from the process environment on first use):
5
- * - `DROPBOX_KEY` + `DROPBOX_SECRET` + `DROPBOX_REFRESH_TOKEN` — long-lived;
6
- * the SDK exchanges the refresh token for short-lived access tokens itself.
7
- * - `DROPBOX_ACCESS_TOKEN` — fallback, e.g. a token generated in the Dropbox
8
- * App Console (expires after ~4h).
9
- */
10
- import { Dropbox } from "dropbox";
11
- export class DropboxAuthFactory {
12
- constructor() { }
13
- static byEnv(env) {
14
- const { DROPBOX_KEY, DROPBOX_SECRET, DROPBOX_REFRESH_TOKEN, DROPBOX_ACCESS_TOKEN, } = env;
15
- if (DROPBOX_KEY && DROPBOX_SECRET && DROPBOX_REFRESH_TOKEN)
16
- return new DropboxAuthClient({
17
- clientId: DROPBOX_KEY,
18
- clientSecret: DROPBOX_SECRET,
19
- refreshToken: DROPBOX_REFRESH_TOKEN,
20
- });
21
- if (DROPBOX_ACCESS_TOKEN)
22
- return new DropboxAuthAccess({ accessToken: DROPBOX_ACCESS_TOKEN });
23
- throw new Error("Dropbox credentials missing: set DROPBOX_KEY + DROPBOX_SECRET + DROPBOX_REFRESH_TOKEN (or DROPBOX_ACCESS_TOKEN)");
24
- }
25
- }
26
- export class IDropboxAuth {
27
- dbx = null;
28
- constructor() { }
29
- client() {
30
- return (this.dbx ??= new Dropbox(this.clientParams()));
31
- }
32
- }
33
- export class DropboxAuthAccess extends IDropboxAuth {
34
- auth;
35
- constructor(auth) {
36
- super();
37
- this.auth = { ...auth };
38
- }
39
- clientParams() {
40
- return { accessToken: this.auth.accessToken };
41
- }
42
- }
43
- export class DropboxAuthClient extends IDropboxAuth {
44
- auth;
45
- constructor(auth) {
46
- super();
47
- this.auth = { ...auth };
48
- }
49
- clientParams() {
50
- return {
51
- clientId: this.auth.clientId,
52
- clientSecret: this.auth.clientSecret,
53
- refreshToken: this.auth.refreshToken,
54
- };
55
- }
56
- }
57
- //# sourceMappingURL=DropboxAuth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DropboxAuth.js","sourceRoot":"","sources":["../../src/client/DropboxAuth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAuB,MAAM,SAAS,CAAC;AAMvD,MAAM,OAAO,kBAAkB;IAC7B,gBAAuB,CAAC;IAEjB,MAAM,CAAC,KAAK,CAAC,GAAuC;QACzD,MAAM,EACJ,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,oBAAoB,GACrB,GAAG,GAAG,CAAC;QAER,IAAI,WAAW,IAAI,cAAc,IAAI,qBAAqB;YACxD,OAAO,IAAI,iBAAiB,CAAC;gBAC3B,QAAQ,EAAE,WAAW;gBACrB,YAAY,EAAE,cAAc;gBAC5B,YAAY,EAAE,qBAAqB;aACpC,CAAC,CAAC;QAEL,IAAI,oBAAoB;YACtB,OAAO,IAAI,iBAAiB,CAAC,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAEtE,MAAM,IAAI,KAAK,CACb,iHAAiH,CAClH,CAAC;IACJ,CAAC;CACF;AAED,MAAM,OAAgB,YAAY;IACtB,GAAG,GAAmB,IAAI,CAAC;IAErC,gBAAsB,CAAC;IAIhB,MAAM;QACX,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,YAAY;IAC9B,IAAI,CAA0B;IAEjD,YAAmB,IAA6B;QAC9C,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC1B,CAAC;IAEkB,YAAY;QAC7B,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAChD,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,YAAY;IAC9B,IAAI,CAA0B;IAEjD,YAAmB,IAA6B;QAC9C,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC1B,CAAC;IAES,YAAY;QACpB,OAAO;YACL,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ;YAC5B,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY;YACpC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY;SACrC,CAAC;IACJ,CAAC;CACF"}
@@ -1,11 +0,0 @@
1
- import type { PicChargers } from "@mxpicture/tesla-superchargers-generated/public";
2
- /** Refetch a cached charger whose last fetch is older than this. */
3
- export declare const CHARGER_STALE_AFTER_DAYS = 5;
4
- /** Detail fetches per findByCoordinate call — the endpoint is slow and guarded. */
5
- export declare const MAX_CHARGER_FETCHES = 3;
6
- /** Candidates that failed this often are tried after the rest, so dead slugs stop burning the fetch budget first. */
7
- export declare const FAILURE_DEPRIORITIZE_THRESHOLD = 3;
8
- export declare const isFresh: (row: PicChargers) => boolean;
9
- /** FK links are not part of the feed; a snapshot re-sync must not wipe them. */
10
- export declare const withoutFkColumns: (columns: string[]) => string[];
11
- //# sourceMappingURL=chargers.common.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chargers.common.d.ts","sourceRoot":"","sources":["../../src/common/chargers.common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iDAAiD,CAAC;AAEnF,oEAAoE;AACpE,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,qHAAqH;AACrH,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAEhD,eAAO,MAAM,OAAO,GAAI,KAAK,WAAW,KAAG,OAEkB,CAAC;AAE9D,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,EAAE,KAAG,MAAM,EACJ,CAAC"}
@@ -1,11 +0,0 @@
1
- /** Refetch a cached charger whose last fetch is older than this. */
2
- export const CHARGER_STALE_AFTER_DAYS = 5;
3
- /** Detail fetches per findByCoordinate call — the endpoint is slow and guarded. */
4
- export const MAX_CHARGER_FETCHES = 3;
5
- /** Candidates that failed this often are tried after the rest, so dead slugs stop burning the fetch budget first. */
6
- export const FAILURE_DEPRIORITIZE_THRESHOLD = 3;
7
- export const isFresh = (row) => row.fetched_at.getTime() >
8
- Date.now() - CHARGER_STALE_AFTER_DAYS * 24 * 60 * 60 * 1000;
9
- /** FK links are not part of the feed; a snapshot re-sync must not wipe them. */
10
- export const withoutFkColumns = (columns) => columns.filter((column) => column !== "pic_slugs_id");
11
- //# sourceMappingURL=chargers.common.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chargers.common.js","sourceRoot":"","sources":["../../src/common/chargers.common.ts"],"names":[],"mappings":"AAEA,oEAAoE;AACpE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAE1C,mFAAmF;AACnF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,qHAAqH;AACrH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC;AAEhD,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAgB,EAAW,EAAE,CACnD,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE;IACxB,IAAI,CAAC,GAAG,EAAE,GAAG,wBAAwB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9D,gFAAgF;AAChF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAiB,EAAY,EAAE,CAC9D,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,cAAc,CAAC,CAAC"}
@@ -1,8 +0,0 @@
1
- import type { PushoverMessage } from "../types/pushover.types.js";
2
- import type { FailedSources } from "../types/failures.types.js";
3
- /**
4
- * Build the Pushover message for a set of failing rows, or null when every
5
- * source is empty (nothing to page). Pure — unit-tested without a request.
6
- */
7
- export declare const buildFailureMessage: (failed: FailedSources) => PushoverMessage | null;
8
- //# sourceMappingURL=failures.common.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"failures.common.d.ts","sourceRoot":"","sources":["../../src/common/failures.common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAehE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,aAAa,KACpB,eAAe,GAAG,IA2CpB,CAAC"}
@@ -1,33 +0,0 @@
1
- /** Max rows listed per source section before truncating with a summary line. */
2
- const MAX_LISTED = 10;
3
- /** One titled section: up to MAX_LISTED lines, then `…and N more.` if truncated. */
4
- const section = (title, lines) => {
5
- const shown = lines.slice(0, MAX_LISTED).join("\n");
6
- const more = lines.length > MAX_LISTED
7
- ? `\n…and ${lines.length - MAX_LISTED} more.`
8
- : "";
9
- return `${title}\n${shown}${more}`;
10
- };
11
- /**
12
- * Build the Pushover message for a set of failing rows, or null when every
13
- * source is empty (nothing to page). Pure — unit-tested without a request.
14
- */
15
- export const buildFailureMessage = (failed) => {
16
- const sections = [];
17
- if (failed.charges.length > 0)
18
- sections.push(section("Charges:", failed.charges.map((c) => `• charge #${c.charging_processes_id} ` +
19
- `(${c.start_date ? c.start_date.toISOString() : "?"}): ` +
20
- `${c.failure_count} failures`)));
21
- if (failed.chargers.length > 0)
22
- sections.push(section("Chargers:", failed.chargers.map((c) => `• ${c.name}, ${c.city}: ${c.failure_count} failures`)));
23
- if (failed.slugs.length > 0)
24
- sections.push(section("Slugs:", failed.slugs.map((s) => `• ${s.location_url_slug}: ${s.failure_count} failures`)));
25
- if (sections.length === 0)
26
- return null;
27
- return {
28
- title: "🚫 Supercharger failures",
29
- message: sections.join("\n\n"),
30
- priority: 1,
31
- };
32
- };
33
- //# sourceMappingURL=failures.common.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"failures.common.js","sourceRoot":"","sources":["../../src/common/failures.common.ts"],"names":[],"mappings":"AAGA,gFAAgF;AAChF,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB,oFAAoF;AACpF,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,KAAe,EAAU,EAAE;IACzD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,IAAI,GACR,KAAK,CAAC,MAAM,GAAG,UAAU;QACvB,CAAC,CAAC,UAAU,KAAK,CAAC,MAAM,GAAG,UAAU,QAAQ;QAC7C,CAAC,CAAC,EAAE,CAAC;IACT,OAAO,GAAG,KAAK,KAAK,KAAK,GAAG,IAAI,EAAE,CAAC;AACrC,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAqB,EACG,EAAE;IAC1B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QAC3B,QAAQ,CAAC,IAAI,CACX,OAAO,CACL,UAAU,EACV,MAAM,CAAC,OAAO,CAAC,GAAG,CAChB,CAAC,CAAC,EAAE,EAAE,CACJ,aAAa,CAAC,CAAC,qBAAqB,GAAG;YACvC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK;YACxD,GAAG,CAAC,CAAC,aAAa,WAAW,CAChC,CACF,CACF,CAAC;IAEJ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;QAC5B,QAAQ,CAAC,IAAI,CACX,OAAO,CACL,WAAW,EACX,MAAM,CAAC,QAAQ,CAAC,GAAG,CACjB,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,aAAa,WAAW,CAC7D,CACF,CACF,CAAC;IAEJ,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACzB,QAAQ,CAAC,IAAI,CACX,OAAO,CACL,QAAQ,EACR,MAAM,CAAC,KAAK,CAAC,GAAG,CACd,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,iBAAiB,KAAK,CAAC,CAAC,aAAa,WAAW,CAC/D,CACF,CACF,CAAC;IAEJ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,OAAO;QACL,KAAK,EAAE,0BAA0B;QACjC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;QAC9B,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,CAAC,CAAC"}
@@ -1,85 +0,0 @@
1
- /**
2
- * Tesla charging-CSV import for pic_invoices.
3
- *
4
- * Pipeline: raw CSV string
5
- * → csv-parse (structure: delimiters, quoting, headers)
6
- * → zod row schema (semantics: "N/A" → null, "22.1285 kwh" → value+unit,
7
- * "0.51/kwh" → value+unit, NUMERIC precision checks, ISO dates)
8
- * → PicInvoicesInitializer
9
- * → parameterized INSERT (numerics stay strings; pg casts to NUMERIC losslessly)
10
- *
11
- * deps: npm i csv-parse postgres zod
12
- */
13
- import type { Invoice, Meta } from "@mxpicture/tesla-superchargers-mate/types";
14
- import { z } from "zod";
15
- export declare const invoicesCsvSchema: z.ZodObject<{
16
- ChargeStartDateTime: z.ZodPipe<z.ZodString, z.ZodTransform<Date, string>>;
17
- Name: z.ZodPreprocess<z.ZodNullable<z.ZodString>>;
18
- Vin: z.ZodString;
19
- Model: z.ZodPreprocess<z.ZodNullable<z.ZodString>>;
20
- Country: z.ZodString;
21
- SiteLocationName: z.ZodString;
22
- Description: z.ZodPreprocess<z.ZodNullable<z.ZodString>>;
23
- QuantityBase: z.ZodPipe<z.ZodString, z.ZodTransform<{
24
- value: number;
25
- unit: string;
26
- }, string>>;
27
- QuantityTier1: z.ZodPreprocess<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<{
28
- value: number;
29
- unit: string;
30
- }, string>>>>;
31
- QuantityTier2: z.ZodPreprocess<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<{
32
- value: number;
33
- unit: string;
34
- }, string>>>>;
35
- QuantityTier3: z.ZodPreprocess<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<{
36
- value: number;
37
- unit: string;
38
- }, string>>>>;
39
- QuantityTier4: z.ZodPreprocess<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<{
40
- value: number;
41
- unit: string;
42
- }, string>>>>;
43
- InvoiceNumber: z.ZodString;
44
- UnitCostBase: z.ZodPipe<z.ZodString, z.ZodTransform<{
45
- value: number;
46
- unit: string;
47
- }, string>>;
48
- UnitCostTier1: z.ZodPreprocess<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<{
49
- value: number;
50
- unit: string;
51
- }, string>>>>;
52
- UnitCostTier2: z.ZodPreprocess<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<{
53
- value: number;
54
- unit: string;
55
- }, string>>>>;
56
- UnitCostTier3: z.ZodPreprocess<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<{
57
- value: number;
58
- unit: string;
59
- }, string>>>>;
60
- UnitCostTier4: z.ZodPreprocess<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<{
61
- value: number;
62
- unit: string;
63
- }, string>>>>;
64
- VAT: z.ZodString;
65
- "Total Exc. VAT": z.ZodString;
66
- "Total Inc. VAT": z.ZodString;
67
- Status: z.ZodPreprocess<z.ZodNullable<z.ZodString>>;
68
- Invoice: z.ZodPreprocess<z.ZodNullable<z.ZodString>>;
69
- }, z.core.$strip>;
70
- export type InvoicesCsv = z.infer<typeof invoicesCsvSchema>;
71
- export interface InvoicesCsvError {
72
- line: number;
73
- issues: z.core.$ZodIssue[];
74
- }
75
- /** One line per invalid row: `line 3: QuantityBase: invalid quantity: "x"`. */
76
- export declare const formatInvoicesCsvErrors: (errors: InvoicesCsvError[]) => string;
77
- export declare const parseInvoicesCsv: (csvText: string) => {
78
- invoices: InvoicesCsv[];
79
- errors: InvoicesCsvError[];
80
- };
81
- export declare const fromInvoiceCsv: ({ csv, meta, }: {
82
- csv: InvoicesCsv;
83
- meta: Meta;
84
- }) => Invoice;
85
- //# sourceMappingURL=invoicesCsv.common.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"invoicesCsv.common.d.ts","sourceRoot":"","sources":["../../src/common/invoicesCsv.common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EACV,OAAO,EAEP,IAAI,EACL,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmGxB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwB5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAM5D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;CAC5B;AAED,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,GAAI,QAAQ,gBAAgB,EAAE,KAAG,MAQtD,CAAC;AAEhB,eAAO,MAAM,gBAAgB,GAC3B,SAAS,MAAM,KACd;IACD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAoB5B,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,gBAG5B;IACD,GAAG,EAAE,WAAW,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ,KAAG,OAgCH,CAAC"}
@@ -1,185 +0,0 @@
1
- /**
2
- * Tesla charging-CSV import for pic_invoices.
3
- *
4
- * Pipeline: raw CSV string
5
- * → csv-parse (structure: delimiters, quoting, headers)
6
- * → zod row schema (semantics: "N/A" → null, "22.1285 kwh" → value+unit,
7
- * "0.51/kwh" → value+unit, NUMERIC precision checks, ISO dates)
8
- * → PicInvoicesInitializer
9
- * → parameterized INSERT (numerics stay strings; pg casts to NUMERIC losslessly)
10
- *
11
- * deps: npm i csv-parse postgres zod
12
- */
13
- import { removeUndefined } from "@mxpicture/tesla-superchargers-mate/transform";
14
- import { parse } from "csv-parse/sync";
15
- import { z } from "zod";
16
- /* ------------------------------------------------------------------ */
17
- /* helpers */
18
- /* ------------------------------------------------------------------ */
19
- const castNum = (name, str) => {
20
- const value = str === null ? str : Number(str);
21
- if (value === null || !Number.isFinite(value))
22
- throw new Error(`invalid ${name}: "${str}"`);
23
- return value;
24
- };
25
- /** "N/A" / "" / whitespace → null, everything else → trimmed string. */
26
- const naToNull = (v) => {
27
- if (typeof v !== "string")
28
- return v;
29
- const t = v.trim();
30
- return t === "" || t.toUpperCase() === "N/A" ? null : t;
31
- };
32
- /**
33
- * Decimal kept as *string* (matches Kanel's typing of pg NUMERIC) with a
34
- * precision/scale check mirroring the column definition, so bad values fail
35
- * in validation instead of as a pg error mid-insert.
36
- */
37
- const numericString = (precision, scale) => z
38
- .string()
39
- .trim()
40
- .regex(/^-?\d+(\.\d+)?$/, "not a decimal number")
41
- .refine((s) => {
42
- const [int = "", frac = ""] = s.replace("-", "").split(".");
43
- return frac.length <= scale && int.length <= precision - scale;
44
- }, `exceeds NUMERIC(${precision}, ${scale})`);
45
- /** "22.1285 kwh" → { value: 22.1285, unit: "kwh" } (unit optional). */
46
- const quantity = z
47
- .string()
48
- .trim()
49
- .transform((s, ctx) => {
50
- const m = s.match(/^(-?\d+(?:\.\d+)?)(?:\s+(\S+))?$/);
51
- if (!m) {
52
- ctx.addIssue({ code: "custom", message: `invalid quantity: "${s}"` });
53
- return z.NEVER;
54
- }
55
- const value = m[1] === null ? m[1] : Number(m[1]);
56
- if (value === null || !Number.isFinite(value)) {
57
- ctx.addIssue({ code: "custom", message: `invalid quantity: "${s}"` });
58
- return z.NEVER;
59
- }
60
- return { value, unit: m[2]?.toLowerCase() ?? null };
61
- })
62
- .refine((q) => numericString(8, 4).safeParse(String(q.value)).success, {
63
- message: "quantity exceeds NUMERIC(8, 4)",
64
- });
65
- /** "0.51/kwh" → { value: 0.51, unit: "kwh" }. */
66
- const unitCost = z
67
- .string()
68
- .trim()
69
- .transform((s, ctx) => {
70
- const m = s.match(/^(-?\d+(?:\.\d+)?)\s*\/\s*(\S+)$/);
71
- if (!m) {
72
- ctx.addIssue({ code: "custom", message: `invalid unit cost: "${s}"` });
73
- return z.NEVER;
74
- }
75
- const value = m[1] === null ? m[1] : Number(m[1]);
76
- if (value === null || !Number.isFinite(value)) {
77
- ctx.addIssue({ code: "custom", message: `invalid unit cost: "${s}"` });
78
- return z.NEVER;
79
- }
80
- return { value, unit: m[2].toLowerCase() };
81
- })
82
- .refine((c) => numericString(4, 2).safeParse(String(c.value)).success, {
83
- message: "unit cost exceeds NUMERIC(4, 2)",
84
- });
85
- /** ISO-8601 with offset ("2026-03-31T14:56:00+02:00") → Date. */
86
- const isoDate = z
87
- .string()
88
- .trim()
89
- .transform((s, ctx) => {
90
- const d = new Date(s);
91
- if (Number.isNaN(d.getTime())) {
92
- ctx.addIssue({ code: "custom", message: `invalid date: "${s}"` });
93
- return z.NEVER;
94
- }
95
- return d;
96
- });
97
- const nullable = (schema) => z.preprocess(naToNull, schema.nullable());
98
- const nonEmpty = z.string().trim().min(1);
99
- /* ------------------------------------------------------------------ */
100
- /* CSV row schema (keys = exact CSV headers) */
101
- /* ------------------------------------------------------------------ */
102
- export const invoicesCsvSchema = z.object({
103
- ChargeStartDateTime: isoDate,
104
- Name: nullable(nonEmpty),
105
- Vin: nonEmpty,
106
- Model: nullable(nonEmpty),
107
- Country: nonEmpty,
108
- SiteLocationName: nonEmpty,
109
- Description: nullable(nonEmpty),
110
- QuantityBase: quantity,
111
- QuantityTier1: nullable(quantity),
112
- QuantityTier2: nullable(quantity),
113
- QuantityTier3: nullable(quantity),
114
- QuantityTier4: nullable(quantity),
115
- InvoiceNumber: nonEmpty,
116
- UnitCostBase: unitCost,
117
- UnitCostTier1: nullable(unitCost),
118
- UnitCostTier2: nullable(unitCost),
119
- UnitCostTier3: nullable(unitCost),
120
- UnitCostTier4: nullable(unitCost),
121
- VAT: numericString(5, 2),
122
- "Total Exc. VAT": numericString(5, 2),
123
- "Total Inc. VAT": numericString(5, 2),
124
- Status: nullable(nonEmpty),
125
- Invoice: nullable(z.string().trim().url()),
126
- });
127
- /** One line per invalid row: `line 3: QuantityBase: invalid quantity: "x"`. */
128
- export const formatInvoicesCsvErrors = (errors) => errors
129
- .map(({ line, issues }) => `line ${line}: ${issues
130
- .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
131
- .join("; ")}`)
132
- .join("\n");
133
- export const parseInvoicesCsv = (csvText) => {
134
- // csv-parse handles quoting ("Windischeschenbach, Germany"), BOM, CRLF.
135
- const records = parse(csvText, {
136
- columns: true, // first line = header, rows become objects
137
- bom: true,
138
- skip_empty_lines: true,
139
- trim: true,
140
- });
141
- const invoices = [];
142
- const errors = [];
143
- records.forEach((record, i) => {
144
- const result = invoicesCsvSchema.safeParse(record);
145
- if (result.success)
146
- invoices.push(result.data);
147
- else
148
- errors.push({ line: i + 2, issues: result.error.issues });
149
- });
150
- return { invoices, errors };
151
- };
152
- export const fromInvoiceCsv = ({ csv, meta, }) => {
153
- const result = {
154
- id: undefined,
155
- chargingProcessesId: null,
156
- fetched_at: meta.fetched_at,
157
- last_fetch_uuid: meta.last_fetch_uuid,
158
- chargeStartDateTime: csv.ChargeStartDateTime,
159
- name: csv.Name,
160
- vin: csv.Vin,
161
- model: csv.Model,
162
- country: csv.Country,
163
- siteLocationName: csv.SiteLocationName,
164
- description: csv.Description,
165
- ucom: csv.QuantityBase.unit,
166
- quantityBase: csv.QuantityBase.value,
167
- quantityTier1: csv.QuantityTier1?.value,
168
- quantityTier2: csv.QuantityTier2?.value,
169
- quantityTier3: csv.QuantityTier3?.value,
170
- quantityTier4: csv.QuantityTier4?.value,
171
- invoiceNumber: csv.InvoiceNumber,
172
- unitCostBase: csv.UnitCostBase.value,
173
- unitCostTier1: csv.UnitCostTier1?.value,
174
- unitCostTier2: csv.UnitCostTier2?.value,
175
- unitCostTier3: csv.UnitCostTier3?.value,
176
- unitCostTier4: csv.UnitCostTier4?.value,
177
- vat: castNum("VAT", csv.VAT),
178
- totalExcVat: castNum("Total Exc. VAT", csv["Total Exc. VAT"]),
179
- totalIncVat: castNum("Total Inc. VAT", csv["Total Inc. VAT"]),
180
- status: csv.Status,
181
- invoice: csv.Invoice,
182
- };
183
- return removeUndefined(result);
184
- };
185
- //# sourceMappingURL=invoicesCsv.common.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"invoicesCsv.common.js","sourceRoot":"","sources":["../../src/common/invoicesCsv.common.ts"],"names":[],"mappings":"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"}
@@ -1,47 +0,0 @@
1
- /**
2
- * Pure pricebook helpers: snapshot-group comparison for the history sync and
3
- * the time-of-use window filter for prices-at-a-time lookups. No DB access —
4
- * unit-testable in isolation.
5
- */
6
- import type { ChargingRate, PricebookContent } from "../types/pricebooks.types.js";
7
- /** Feed values identifying the row that prices a Tesla vehicle's charging. */
8
- export declare const TESLA_VEHICLE_MAKE = "tsla";
9
- export declare const CHARGING_FEE_TYPE = "charging";
10
- export declare const KWH_UOM = "kwh";
11
- /** Canonical `HH:MM:SS` for a TIME value (Date → its UTC wall time). */
12
- export declare const normalizeTime: (time: Date | string) => string;
13
- export declare const normalizeRate: (rate: string | number) => string;
14
- /** One row's content columns as a canonical string, order-independent in `days`. */
15
- export declare const canonicalRow: (row: PricebookContent) => string;
16
- /** Local weekday (0=Sunday … 6=Saturday, matching the feed's `days`) and `HH:MM:SS` of `at` in an IANA time zone. */
17
- export declare const localDayTime: (at: Date, timeZone: string) => {
18
- weekday: number;
19
- time: string;
20
- };
21
- /**
22
- * Does this row's time-of-use window cover the instant `at` (in the charger's
23
- * time zone)? Empty `days` means no day restriction (non-TOU rows). The feed
24
- * partitions a day into windows that never span into the next day, with
25
- * `00:00` as `end` meaning end-of-day (e.g. `00:00–16:00`, `16:00–20:00`,
26
- * `20:00–00:00`); `start == end == 00:00` is a whole-day window. `end` is
27
- * exclusive — each instant falls into exactly one window of a partition.
28
- */
29
- export declare const pricebookCoversInstant: (row: Pick<PricebookContent, "days" | "start_time" | "end_time">, at: Date, timeZone: string) => boolean;
30
- /**
31
- * Order-independent multiset equality over the content columns — the "did any
32
- * price change?" check of the history sync. Meta columns (`fetched_at`,
33
- * `last_fetch_uuid`, ids) are ignored. Normalization matters: a false
34
- * "different" here creates a bogus new group on every fetch.
35
- */
36
- export declare const samePricebookGroup: (a: PricebookContent[], b: PricebookContent[]) => boolean;
37
- /**
38
- * The per-kWh rate a Tesla vehicle pays for charging, picked from the
39
- * variant rows valid at one instant (member/non-member × vehicle make ×
40
- * fee type, as `findPrices` returns them): fee type "charging", vehicle
41
- * make "tesla", the non-member row when both memberships are present
42
- * (Tesla drivers need no membership). Returns null when no such row
43
- * exists or the row doesn't bill per kWh — per-minute billing depends on
44
- * charging power, which stored energy totals don't carry.
45
- */
46
- export declare const teslaChargingRate: (rows: PricebookContent[]) => ChargingRate | null;
47
- //# sourceMappingURL=pricebooks.common.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pricebooks.common.d.ts","sourceRoot":"","sources":["../../src/common/pricebooks.common.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AAEtC,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAC5C,eAAO,MAAM,OAAO,QAAQ,CAAC;AAE7B,wEAAwE;AACxE,eAAO,MAAM,aAAa,GAAI,MAAM,IAAI,GAAG,MAAM,KAAG,MAInD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,GAAG,MAAM,KAAG,MAC7B,CAAC;AAE1B,oFAAoF;AACpF,eAAO,MAAM,YAAY,GAAI,KAAK,gBAAgB,KAAG,MAcxC,CAAC;AAEd,qHAAqH;AACrH,eAAO,MAAM,YAAY,GACvB,IAAI,IAAI,EACR,UAAU,MAAM,KACf;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAkBjC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GACjC,KAAK,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,CAAC,EAC/D,IAAI,IAAI,EACR,UAAU,MAAM,KACf,OASF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,GAAG,gBAAgB,EAAE,EACrB,GAAG,gBAAgB,EAAE,KACpB,OAKF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,gBAAgB,EAAE,KACvB,YAAY,GAAG,IAWjB,CAAC"}
@@ -1,95 +0,0 @@
1
- /** Feed values identifying the row that prices a Tesla vehicle's charging. */
2
- export const TESLA_VEHICLE_MAKE = "tsla";
3
- export const CHARGING_FEE_TYPE = "charging";
4
- export const KWH_UOM = "kwh";
5
- /** Canonical `HH:MM:SS` for a TIME value (Date → its UTC wall time). */
6
- export const normalizeTime = (time) => {
7
- if (time instanceof Date)
8
- return time.toISOString().slice(11, 19); // transforms build Date(0) + UTC hours
9
- const [h = "0", m = "0", s = "0"] = time.split(".")[0].split(":");
10
- return [h, m, s].map((part) => part.padStart(2, "0")).join(":");
11
- };
12
- export const normalizeRate = (rate) => Number(rate).toFixed(2);
13
- /** One row's content columns as a canonical string, order-independent in `days`. */
14
- export const canonicalRow = (row) => [
15
- row.currency_code,
16
- row.uom,
17
- [...row.days].sort((a, b) => a - b).join(","),
18
- normalizeTime(row.start_time),
19
- normalizeTime(row.end_time),
20
- row.vehicle_make_type,
21
- row.fee_type,
22
- row.is_member_pricebook,
23
- row.is_tou,
24
- normalizeRate(row.rate_base),
25
- normalizeRate(row.rate_tier_1),
26
- normalizeRate(row.rate_tier_2),
27
- ].join("|");
28
- /** Local weekday (0=Sunday … 6=Saturday, matching the feed's `days`) and `HH:MM:SS` of `at` in an IANA time zone. */
29
- export const localDayTime = (at, timeZone) => {
30
- const parts = new Intl.DateTimeFormat("en-US", {
31
- timeZone,
32
- weekday: "short",
33
- hourCycle: "h23",
34
- hour: "2-digit",
35
- minute: "2-digit",
36
- second: "2-digit",
37
- }).formatToParts(at);
38
- const part = (type) => parts.find((p) => p.type === type)?.value ?? "";
39
- const weekday = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].indexOf(part("weekday"));
40
- return {
41
- weekday,
42
- time: `${part("hour")}:${part("minute")}:${part("second")}`,
43
- };
44
- };
45
- /**
46
- * Does this row's time-of-use window cover the instant `at` (in the charger's
47
- * time zone)? Empty `days` means no day restriction (non-TOU rows). The feed
48
- * partitions a day into windows that never span into the next day, with
49
- * `00:00` as `end` meaning end-of-day (e.g. `00:00–16:00`, `16:00–20:00`,
50
- * `20:00–00:00`); `start == end == 00:00` is a whole-day window. `end` is
51
- * exclusive — each instant falls into exactly one window of a partition.
52
- */
53
- export const pricebookCoversInstant = (row, at, timeZone) => {
54
- const { weekday, time } = localDayTime(at, timeZone);
55
- if (row.days.length > 0 && !row.days.includes(weekday))
56
- return false;
57
- const start = normalizeTime(row.start_time);
58
- const end = normalizeTime(row.end_time);
59
- if (start === "00:00:00" && end === "00:00:00")
60
- return true; // whole day
61
- if (start > end)
62
- return time >= start || time < end; // end 00:00 = end of day
63
- return time >= start && time < end;
64
- };
65
- /**
66
- * Order-independent multiset equality over the content columns — the "did any
67
- * price change?" check of the history sync. Meta columns (`fetched_at`,
68
- * `last_fetch_uuid`, ids) are ignored. Normalization matters: a false
69
- * "different" here creates a bogus new group on every fetch.
70
- */
71
- export const samePricebookGroup = (a, b) => {
72
- if (a.length !== b.length)
73
- return false;
74
- const canonical = (rows) => rows.map(canonicalRow).sort().join("\n");
75
- return canonical(a) === canonical(b);
76
- };
77
- /**
78
- * The per-kWh rate a Tesla vehicle pays for charging, picked from the
79
- * variant rows valid at one instant (member/non-member × vehicle make ×
80
- * fee type, as `findPrices` returns them): fee type "charging", vehicle
81
- * make "tesla", the non-member row when both memberships are present
82
- * (Tesla drivers need no membership). Returns null when no such row
83
- * exists or the row doesn't bill per kWh — per-minute billing depends on
84
- * charging power, which stored energy totals don't carry.
85
- */
86
- export const teslaChargingRate = (rows) => {
87
- const candidates = rows.filter((row) => row.fee_type.toLowerCase() === CHARGING_FEE_TYPE &&
88
- row.vehicle_make_type.toLowerCase() === TESLA_VEHICLE_MAKE);
89
- const row = candidates.find((candidate) => !candidate.is_member_pricebook) ??
90
- candidates[0];
91
- if (!row || row.uom.toLowerCase() !== KWH_UOM)
92
- return null;
93
- return { rate: Number(row.rate_base), currencyCode: row.currency_code };
94
- };
95
- //# sourceMappingURL=pricebooks.common.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pricebooks.common.js","sourceRoot":"","sources":["../../src/common/pricebooks.common.ts"],"names":[],"mappings":"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"}
@@ -1,23 +0,0 @@
1
- /**
2
- * Minimal Pushover (https://pushover.net) sender. Mirrors the shape used by the
3
- * sibling deployment scripts (a token/user app message with an optional
4
- * priority), so it reuses the same PUSHOVER_* credentials the deployment already
5
- * injects. Sending NEVER throws — a notify failure is logged and reported via
6
- * the return value so it can't abort the run that is trying to alert. When
7
- * either credential is missing the whole feature is a no-op.
8
- */
9
- import type { PushoverConfig, PushoverMessage } from "../types/pushover.types.js";
10
- /**
11
- * Pushover credentials from `PUSHOVER_API_TOKEN` / `PUSHOVER_USER_KEY`, or null
12
- * when either is unset (so the caller treats alerting as disabled).
13
- */
14
- export declare const pushoverConfigFromEnv: () => PushoverConfig | null;
15
- /** The form body Pushover expects. Pure, so it is unit-tested without a request. */
16
- export declare const buildPushoverForm: (cfg: PushoverConfig, msg: PushoverMessage) => Record<string, string>;
17
- /**
18
- * POST one message to Pushover. Returns true on success; on any failure it logs
19
- * via `logger` and returns false (never throws), so a notify error can't kill
20
- * the caller.
21
- */
22
- export declare const sendPushover: (cfg: PushoverConfig, msg: PushoverMessage, logger?: (m: string) => void) => Promise<boolean>;
23
- //# sourceMappingURL=pushover.common.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pushover.common.d.ts","sourceRoot":"","sources":["../../src/common/pushover.common.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAChB,MAAM,4BAA4B,CAAC;AAIpC;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAO,cAAc,GAAG,IAKzD,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,GAC5B,KAAK,cAAc,EACnB,KAAK,eAAe,KACnB,MAAM,CAAC,MAAM,EAAE,MAAM,CAMtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAQ,CAAC,CAAC,EAAE,MAAM,KAAK,IAAe,KACrC,OAAO,CAAC,OAAO,CAkBjB,CAAC"}
@@ -1,52 +0,0 @@
1
- /**
2
- * Minimal Pushover (https://pushover.net) sender. Mirrors the shape used by the
3
- * sibling deployment scripts (a token/user app message with an optional
4
- * priority), so it reuses the same PUSHOVER_* credentials the deployment already
5
- * injects. Sending NEVER throws — a notify failure is logged and reported via
6
- * the return value so it can't abort the run that is trying to alert. When
7
- * either credential is missing the whole feature is a no-op.
8
- */
9
- const PUSHOVER_URL = "https://api.pushover.net/1/messages.json";
10
- /**
11
- * Pushover credentials from `PUSHOVER_API_TOKEN` / `PUSHOVER_USER_KEY`, or null
12
- * when either is unset (so the caller treats alerting as disabled).
13
- */
14
- export const pushoverConfigFromEnv = () => {
15
- const token = process.env.PUSHOVER_API_TOKEN;
16
- const user = process.env.PUSHOVER_USER_KEY;
17
- if (!token || !user)
18
- return null;
19
- return { token, user };
20
- };
21
- /** The form body Pushover expects. Pure, so it is unit-tested without a request. */
22
- export const buildPushoverForm = (cfg, msg) => ({
23
- token: cfg.token,
24
- user: cfg.user,
25
- title: msg.title,
26
- message: msg.message,
27
- priority: String(msg.priority ?? 0),
28
- });
29
- /**
30
- * POST one message to Pushover. Returns true on success; on any failure it logs
31
- * via `logger` and returns false (never throws), so a notify error can't kill
32
- * the caller.
33
- */
34
- export const sendPushover = async (cfg, msg, logger = () => { }) => {
35
- try {
36
- const res = await fetch(PUSHOVER_URL, {
37
- method: "POST",
38
- body: new URLSearchParams(buildPushoverForm(cfg, msg)),
39
- });
40
- if (!res.ok) {
41
- logger(`pushover send failed: HTTP ${res.status}`);
42
- return false;
43
- }
44
- logger(`pushover sent: ${msg.title}`);
45
- return true;
46
- }
47
- catch (e) {
48
- logger(`pushover send failed: ${e instanceof Error ? e.message : String(e)}`);
49
- return false;
50
- }
51
- };
52
- //# sourceMappingURL=pushover.common.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pushover.common.js","sourceRoot":"","sources":["../../src/common/pushover.common.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,YAAY,GAAG,0CAA0C,CAAC;AAEhE;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAA0B,EAAE;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC3C,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,GAAmB,EACnB,GAAoB,EACI,EAAE,CAAC,CAAC;IAC5B,KAAK,EAAE,GAAG,CAAC,KAAK;IAChB,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,KAAK,EAAE,GAAG,CAAC,KAAK;IAChB,OAAO,EAAE,GAAG,CAAC,OAAO;IACpB,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC;CACpC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,GAAmB,EACnB,GAAoB,EACpB,SAA8B,GAAG,EAAE,GAAE,CAAC,EACpB,EAAE;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE;YACpC,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,eAAe,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;SACvD,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,8BAA8B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACnD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,CAAC,kBAAkB,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CACJ,yBAAyB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACtE,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC"}
@@ -1,4 +0,0 @@
1
- /** Abort the sync when the feed shrinks below this share of the active rows. */
2
- export declare const CIRCUIT_BREAKER_MIN_RATIO = 0.5;
3
- export declare const SYNC_META_COLUMNS: string[];
4
- //# sourceMappingURL=sync.common.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sync.common.d.ts","sourceRoot":"","sources":["../../src/common/sync.common.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,eAAO,MAAM,iBAAiB,UAM7B,CAAC"}
@@ -1,10 +0,0 @@
1
- /** Abort the sync when the feed shrinks below this share of the active rows. */
2
- export const CIRCUIT_BREAKER_MIN_RATIO = 0.5;
3
- export const SYNC_META_COLUMNS = [
4
- "fetched_at",
5
- "last_fetch_uuid",
6
- "failure_count",
7
- "failure_notified_at",
8
- "deleted_at",
9
- ];
10
- //# sourceMappingURL=sync.common.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sync.common.js","sourceRoot":"","sources":["../../src/common/sync.common.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAE7C,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY;IACZ,iBAAiB;IACjB,eAAe;IACf,qBAAqB;IACrB,YAAY;CACb,CAAC"}