@mxpicture/tesla-superchargers-mate 0.1.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.
Files changed (109) hide show
  1. package/dist/client/TeslaClient.d.ts +49 -0
  2. package/dist/client/TeslaClient.d.ts.map +1 -0
  3. package/dist/client/TeslaClient.js +81 -0
  4. package/dist/client/TeslaClient.js.map +1 -0
  5. package/dist/client/chargers.clients.d.ts +188 -0
  6. package/dist/client/chargers.clients.d.ts.map +1 -0
  7. package/dist/client/chargers.clients.js +114 -0
  8. package/dist/client/chargers.clients.js.map +1 -0
  9. package/dist/client/common.clients.d.ts +34 -0
  10. package/dist/client/common.clients.d.ts.map +1 -0
  11. package/dist/client/common.clients.js +129 -0
  12. package/dist/client/common.clients.js.map +1 -0
  13. package/dist/client/index.d.ts +5 -0
  14. package/dist/client/index.d.ts.map +1 -0
  15. package/dist/client/index.js +6 -0
  16. package/dist/client/index.js.map +1 -0
  17. package/dist/client/slugs.clients.d.ts +48 -0
  18. package/dist/client/slugs.clients.d.ts.map +1 -0
  19. package/dist/client/slugs.clients.js +57 -0
  20. package/dist/client/slugs.clients.js.map +1 -0
  21. package/dist/common/coordinates.d.ts +31 -0
  22. package/dist/common/coordinates.d.ts.map +1 -0
  23. package/dist/common/coordinates.js +44 -0
  24. package/dist/common/coordinates.js.map +1 -0
  25. package/dist/common/index.d.ts +2 -0
  26. package/dist/common/index.d.ts.map +1 -0
  27. package/dist/common/index.js +3 -0
  28. package/dist/common/index.js.map +1 -0
  29. package/dist/store/ChargingProcessesStore.d.ts +34 -0
  30. package/dist/store/ChargingProcessesStore.d.ts.map +1 -0
  31. package/dist/store/ChargingProcessesStore.js +90 -0
  32. package/dist/store/ChargingProcessesStore.js.map +1 -0
  33. package/dist/store/GeofencesStore.d.ts +20 -0
  34. package/dist/store/GeofencesStore.d.ts.map +1 -0
  35. package/dist/store/GeofencesStore.js +28 -0
  36. package/dist/store/GeofencesStore.js.map +1 -0
  37. package/dist/store/IFailableStore.d.ts +29 -0
  38. package/dist/store/IFailableStore.d.ts.map +1 -0
  39. package/dist/store/IFailableStore.js +47 -0
  40. package/dist/store/IFailableStore.js.map +1 -0
  41. package/dist/store/IMateStore.d.ts +66 -0
  42. package/dist/store/IMateStore.d.ts.map +1 -0
  43. package/dist/store/IMateStore.js +132 -0
  44. package/dist/store/IMateStore.js.map +1 -0
  45. package/dist/store/PicChargersStore.d.ts +17 -0
  46. package/dist/store/PicChargersStore.d.ts.map +1 -0
  47. package/dist/store/PicChargersStore.js +34 -0
  48. package/dist/store/PicChargersStore.js.map +1 -0
  49. package/dist/store/PicPricebooksStore.d.ts +27 -0
  50. package/dist/store/PicPricebooksStore.d.ts.map +1 -0
  51. package/dist/store/PicPricebooksStore.js +57 -0
  52. package/dist/store/PicPricebooksStore.js.map +1 -0
  53. package/dist/store/PicSlugsStore.d.ts +18 -0
  54. package/dist/store/PicSlugsStore.d.ts.map +1 -0
  55. package/dist/store/PicSlugsStore.js +34 -0
  56. package/dist/store/PicSlugsStore.js.map +1 -0
  57. package/dist/store/db.d.ts +15 -0
  58. package/dist/store/db.d.ts.map +1 -0
  59. package/dist/store/db.js +26 -0
  60. package/dist/store/db.js.map +1 -0
  61. package/dist/store/index.d.ts +10 -0
  62. package/dist/store/index.d.ts.map +1 -0
  63. package/dist/store/index.js +11 -0
  64. package/dist/store/index.js.map +1 -0
  65. package/dist/store/statements.d.ts +14 -0
  66. package/dist/store/statements.d.ts.map +1 -0
  67. package/dist/store/statements.js +30 -0
  68. package/dist/store/statements.js.map +1 -0
  69. package/dist/transform/chargers.transform.d.ts +9 -0
  70. package/dist/transform/chargers.transform.d.ts.map +1 -0
  71. package/dist/transform/chargers.transform.js +42 -0
  72. package/dist/transform/chargers.transform.js.map +1 -0
  73. package/dist/transform/common.transform.d.ts +19 -0
  74. package/dist/transform/common.transform.d.ts.map +1 -0
  75. package/dist/transform/common.transform.js +72 -0
  76. package/dist/transform/common.transform.js.map +1 -0
  77. package/dist/transform/index.d.ts +5 -0
  78. package/dist/transform/index.d.ts.map +1 -0
  79. package/dist/transform/index.js +6 -0
  80. package/dist/transform/index.js.map +1 -0
  81. package/dist/transform/pricebooks.transform.d.ts +9 -0
  82. package/dist/transform/pricebooks.transform.d.ts.map +1 -0
  83. package/dist/transform/pricebooks.transform.js +24 -0
  84. package/dist/transform/pricebooks.transform.js.map +1 -0
  85. package/dist/transform/slugs.transform.d.ts +9 -0
  86. package/dist/transform/slugs.transform.d.ts.map +1 -0
  87. package/dist/transform/slugs.transform.js +29 -0
  88. package/dist/transform/slugs.transform.js.map +1 -0
  89. package/dist/types/charger.types.d.ts +5 -0
  90. package/dist/types/charger.types.d.ts.map +1 -0
  91. package/dist/types/charger.types.js +2 -0
  92. package/dist/types/charger.types.js.map +1 -0
  93. package/dist/types/chargingProcess.types.d.ts +21 -0
  94. package/dist/types/chargingProcess.types.d.ts.map +1 -0
  95. package/dist/types/chargingProcess.types.js +2 -0
  96. package/dist/types/chargingProcess.types.js.map +1 -0
  97. package/dist/types/client.types.d.ts +14 -0
  98. package/dist/types/client.types.d.ts.map +1 -0
  99. package/dist/types/client.types.js +2 -0
  100. package/dist/types/client.types.js.map +1 -0
  101. package/dist/types/index.d.ts +5 -0
  102. package/dist/types/index.d.ts.map +1 -0
  103. package/dist/types/index.js +6 -0
  104. package/dist/types/index.js.map +1 -0
  105. package/dist/types/slug.types.d.ts +5 -0
  106. package/dist/types/slug.types.d.ts.map +1 -0
  107. package/dist/types/slug.types.js +2 -0
  108. package/dist/types/slug.types.js.map +1 -0
  109. package/package.json +61 -0
@@ -0,0 +1,48 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * ---------------------------------------------------------------------------------
4
+ * ----------------------- slug ----------------------------------------------------
5
+ * ---------------------------------------------------------------------------------
6
+ */
7
+ export declare const teslaClientSlugSchema: z.ZodObject<{
8
+ inCN: z.ZodBoolean;
9
+ inHkMoTw: z.ZodBoolean;
10
+ latitude: z.ZodNumber;
11
+ longitude: z.ZodNumber;
12
+ location_type: z.ZodArray<z.ZodString>;
13
+ location_url_slug: z.ZodString;
14
+ supercharger_function: z.ZodOptional<z.ZodObject<{
15
+ access_type: z.ZodString;
16
+ actual_latitude: z.ZodString;
17
+ actual_longitude: z.ZodString;
18
+ charging_accessibility: z.ZodString;
19
+ coming_soon_latitude: z.ZodNullable<z.ZodString>;
20
+ coming_soon_longitude: z.ZodNullable<z.ZodString>;
21
+ coming_soon_name: z.ZodNullable<z.ZodString>;
22
+ open_to_non_tesla: z.ZodBoolean;
23
+ project_status: z.ZodString;
24
+ show_on_find_us: z.ZodString;
25
+ site_status: z.ZodString;
26
+ vote_winner_quarter: z.ZodNullable<z.ZodString>;
27
+ }, z.core.$strip>>;
28
+ title: z.ZodString;
29
+ uuid: z.ZodString;
30
+ }, z.core.$strip>;
31
+ export type TeslaClientSlug = z.infer<typeof teslaClientSlugSchema>;
32
+ export declare const teslaClientSlugsResponseSchema: z.ZodObject<{
33
+ data: z.ZodObject<{
34
+ data: z.ZodArray<z.ZodUnknown>;
35
+ success: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
36
+ }, z.core.$strip>;
37
+ }, z.core.$strip>;
38
+ export type TeslaClientSlugsResponse = z.infer<typeof teslaClientSlugsResponseSchema>;
39
+ /**
40
+ * Per-element validation of a slugs feed array: a malformed entry (schema
41
+ * drift on a single site) is skipped and counted instead of aborting the
42
+ * whole sync. Throws only when `entries` is not an array at all.
43
+ */
44
+ export declare const parseTeslaClientSlugs: (entries: unknown) => {
45
+ slugs: TeslaClientSlug[];
46
+ invalid: number;
47
+ };
48
+ //# sourceMappingURL=slugs.clients.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugs.clients.d.ts","sourceRoot":"","sources":["../../src/client/slugs.clients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;iBAyBhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAGpE,eAAO,MAAM,8BAA8B;;;;;iBAKzC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAC5C,OAAO,8BAA8B,CACtC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,OAAO,KACf;IAAE,KAAK,EAAE,eAAe,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAS7C,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * ---------------------------------------------------------------------------------
4
+ * ----------------------- slug ----------------------------------------------------
5
+ * ---------------------------------------------------------------------------------
6
+ */
7
+ export const teslaClientSlugSchema = z.object({
8
+ inCN: z.boolean(),
9
+ inHkMoTw: z.boolean(),
10
+ latitude: z.number(),
11
+ longitude: z.number(),
12
+ location_type: z.array(z.string()),
13
+ location_url_slug: z.string(),
14
+ supercharger_function: z
15
+ .object({
16
+ access_type: z.string(),
17
+ actual_latitude: z.string(),
18
+ actual_longitude: z.string(),
19
+ charging_accessibility: z.string(),
20
+ coming_soon_latitude: z.string().nullable(),
21
+ coming_soon_longitude: z.string().nullable(),
22
+ coming_soon_name: z.string().nullable(),
23
+ open_to_non_tesla: z.boolean(),
24
+ project_status: z.string(),
25
+ show_on_find_us: z.string(),
26
+ site_status: z.string(),
27
+ vote_winner_quarter: z.string().nullable(),
28
+ })
29
+ .optional(),
30
+ title: z.string(),
31
+ uuid: z.string(),
32
+ });
33
+ // elements stay unknown: they are validated one by one via parseTeslaClientSlugs
34
+ export const teslaClientSlugsResponseSchema = z.object({
35
+ data: z.object({
36
+ data: z.array(z.unknown()),
37
+ success: z.boolean().nullable().optional(),
38
+ }),
39
+ });
40
+ /**
41
+ * Per-element validation of a slugs feed array: a malformed entry (schema
42
+ * drift on a single site) is skipped and counted instead of aborting the
43
+ * whole sync. Throws only when `entries` is not an array at all.
44
+ */
45
+ export const parseTeslaClientSlugs = (entries) => {
46
+ const slugs = [];
47
+ let invalid = 0;
48
+ for (const entry of z.array(z.unknown()).parse(entries)) {
49
+ const parsed = teslaClientSlugSchema.safeParse(entry);
50
+ if (parsed.success)
51
+ slugs.push(parsed.data);
52
+ else
53
+ invalid++;
54
+ }
55
+ return { slugs, invalid };
56
+ };
57
+ //# sourceMappingURL=slugs.clients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugs.clients.js","sourceRoot":"","sources":["../../src/client/slugs.clients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;IACjB,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;IACrB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE;IAC7B,qBAAqB,EAAE,CAAC;SACrB,MAAM,CAAC;QACN,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;QAC3B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE;QAC5B,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE;QAClC,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3C,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC5C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACvC,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE;QAC9B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;QAC1B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;QAC3B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC3C,CAAC;SACD,QAAQ,EAAE;IACb,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAC;AAIH,iFAAiF;AACjF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;KAC3C,CAAC;CACH,CAAC,CAAC;AAMH;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,OAAgB,EAC+B,EAAE;IACjD,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,MAAM,CAAC,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;;YACvC,OAAO,EAAE,CAAC;IACjB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC5B,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ export interface Coordinate {
2
+ latitude: number;
3
+ longitude: number;
4
+ }
5
+ export interface CoordinateStr {
6
+ latitude: string;
7
+ longitude: string;
8
+ }
9
+ export interface CoordinateAny {
10
+ latitude: string | number;
11
+ longitude: string | number;
12
+ }
13
+ /**
14
+ * Coordinate box (degrees) used as a cheap SQL prefilter before the exact
15
+ * haversine check below. Latitude degrees are ~constant (0.005° ≈ 555 m);
16
+ * longitude degrees shrink with latitude, so the longitude bound is twice as
17
+ * wide — 0.01° still spans the 250 m match radius up to ~77° latitude,
18
+ * comfortably past the northernmost Superchargers.
19
+ */
20
+ export declare const COVERED_RADIUS_KM = 0.25;
21
+ export declare const COVERED_MARGIN = 1.05;
22
+ export declare const EARTH_RADIUS_KM = 6371;
23
+ export declare const KM_PER_DEG_LAT = 111.32;
24
+ export declare const distanceInKm: (a: CoordinateAny, b: CoordinateAny) => number;
25
+ export declare const toCoordinate: (item: CoordinateAny) => Coordinate;
26
+ export declare const toNumber: (value: string | number) => number;
27
+ export declare const boundingBoxDeg: (item: CoordinateAny, radiusKm: number) => {
28
+ latDeg: number;
29
+ lonDeg: number;
30
+ };
31
+ //# sourceMappingURL=coordinates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinates.d.ts","sourceRoot":"","sources":["../../src/common/coordinates.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,eAAO,MAAM,cAAc,SAAS,CAAC;AAIrC,eAAO,MAAM,YAAY,GAAI,GAAG,aAAa,EAAE,GAAG,aAAa,KAAG,MAgBjE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,MAAM,aAAa,KAAG,UAGjD,CAAC;AAEH,eAAO,MAAM,QAAQ,GAAI,OAAO,MAAM,GAAG,MAAM,KAAG,MAQjD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,aAAa,EAAE,UAAU,MAAM;;;CAOnE,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Coordinate box (degrees) used as a cheap SQL prefilter before the exact
3
+ * haversine check below. Latitude degrees are ~constant (0.005° ≈ 555 m);
4
+ * longitude degrees shrink with latitude, so the longitude bound is twice as
5
+ * wide — 0.01° still spans the 250 m match radius up to ~77° latitude,
6
+ * comfortably past the northernmost Superchargers.
7
+ */
8
+ export const COVERED_RADIUS_KM = 0.25;
9
+ export const COVERED_MARGIN = 1.05; // small safety factor for the box
10
+ export const EARTH_RADIUS_KM = 6371;
11
+ export const KM_PER_DEG_LAT = 111.32; // near-constant everywhere
12
+ const toRadians = (degrees) => degrees * (Math.PI / 180);
13
+ export const distanceInKm = (a, b) => {
14
+ const aCoord = toCoordinate(a);
15
+ const bCoord = toCoordinate(b);
16
+ const dLat = toRadians(bCoord.latitude - aCoord.latitude);
17
+ const dLon = toRadians(bCoord.longitude - aCoord.longitude);
18
+ const lat1 = toRadians(aCoord.latitude);
19
+ const lat2 = toRadians(bCoord.latitude);
20
+ const h = Math.sin(dLat / 2) ** 2 +
21
+ Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2;
22
+ const c = 2 * Math.asin(Math.sqrt(h));
23
+ return EARTH_RADIUS_KM * c;
24
+ };
25
+ export const toCoordinate = (item) => ({
26
+ latitude: toNumber(item.latitude),
27
+ longitude: toNumber(item.longitude),
28
+ });
29
+ export const toNumber = (value) => {
30
+ const n = typeof value === "number" ? value : parseFloat(value);
31
+ if (Number.isNaN(n)) {
32
+ throw new Error(`Could not parse a numeric value from: ${JSON.stringify(value)}`);
33
+ }
34
+ return n;
35
+ };
36
+ export const boundingBoxDeg = (item, radiusKm) => {
37
+ const coord = toCoordinate(item);
38
+ const latDeg = radiusKm / KM_PER_DEG_LAT;
39
+ const cosLat = Math.cos((coord.latitude * Math.PI) / 180);
40
+ // guard: near the poles cos(lat) -> 0 and lonDeg explodes; clamp to full range
41
+ const lonDeg = cosLat > 1e-6 ? radiusKm / (KM_PER_DEG_LAT * cosLat) : 180;
42
+ return { latDeg, lonDeg };
43
+ };
44
+ //# sourceMappingURL=coordinates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinates.js","sourceRoot":"","sources":["../../src/common/coordinates.ts"],"names":[],"mappings":"AAeA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,CAAC,kCAAkC;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC,2BAA2B;AAEjE,MAAM,SAAS,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;AAEzE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAgB,EAAE,CAAgB,EAAU,EAAE;IACzE,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAE5D,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAExC,MAAM,CAAC,GACL,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IAE5D,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtC,OAAO,eAAe,GAAG,CAAC,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAmB,EAAc,EAAE,CAAC,CAAC;IAChE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;IACjC,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAsB,EAAU,EAAE;IACzD,MAAM,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,yCAAyC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAmB,EAAE,QAAgB,EAAE,EAAE;IACtE,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,QAAQ,GAAG,cAAc,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC;IAC1D,+EAA+E;IAC/E,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,cAAc,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./coordinates.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ // This file is auto-generated by build-cli. Do not edit manually.
2
+ export * from "./coordinates.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { type ChargingProcesses, type ChargingProcessesId, type ChargingProcessesInitializer, type ChargingProcessesMutator, type PicChargingProcessStates, type PicChargingProcessStatesId } from "@mxpicture/tesla-superchargers-generated/public";
2
+ import { IMateStore } from "./IMateStore.js";
3
+ import { type SqlTx } from "./db.js";
4
+ import type { FailedChargingProcessState, UnpricedChargingProcess } from "../types/chargingProcess.types.js";
5
+ /**
6
+ * Access to TeslaMate's `charging_processes` table. The helpers here
7
+ * ({@link ChargingProcessesStore.getUnpricedCharges},
8
+ * {@link ChargingProcessesStore.updatePrice}) will back the planned charge-cost
9
+ * backfill; nothing calls them yet. The backfill's guardrails, once wired up:
10
+ * only charges matched to one of our priced Superchargers, and only rows where
11
+ * `cost IS NULL` — an existing cost (TeslaMate's or the user's) is never
12
+ * overwritten.
13
+ */
14
+ export declare class ChargingProcessesStore extends IMateStore<ChargingProcessesId, ChargingProcesses, ChargingProcessesInitializer, ChargingProcessesMutator> {
15
+ readonly statesTable: string;
16
+ constructor();
17
+ getUnpricedCharges(tx?: SqlTx): Promise<UnpricedChargingProcess[]>;
18
+ updatePrice(processId: ChargingProcessesId, cost: number | null, tx?: SqlTx): Promise<ChargingProcesses>;
19
+ getState(processId: ChargingProcessesId, tx?: SqlTx): Promise<PicChargingProcessStates | null>;
20
+ resetState(processId: ChargingProcessesId, tx?: SqlTx): Promise<void>;
21
+ /**
22
+ * Failed state rows at or above `threshold`, most-failing first, each joined
23
+ * to its charge's `start_date`. With `onlyUnnotified` only rows not yet paged
24
+ * (`failure_notified_at IS NULL`) are returned.
25
+ */
26
+ getFailedStates(threshold: number, onlyUnnotified?: boolean, tx?: SqlTx): Promise<FailedChargingProcessState[]>;
27
+ /** Stamp the notified flag on the given state rows (by id; empty is a no-op). */
28
+ markStatesNotified(stateIds: PicChargingProcessStatesId[], tx?: SqlTx): Promise<void>;
29
+ /** Bulk reset = delete every state row (matching single-row resetState); returns rows deleted. */
30
+ resetAllStates(tx?: SqlTx): Promise<number>;
31
+ incrementStateFailed(processId: ChargingProcessesId, tx?: SqlTx): Promise<number>;
32
+ }
33
+ export declare const chargingProcessesStore: () => ChargingProcessesStore;
34
+ //# sourceMappingURL=ChargingProcessesStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChargingProcessesStore.d.ts","sourceRoot":"","sources":["../../src/store/ChargingProcessesStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,4BAA4B,EACjC,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAChC,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAO,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EACV,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,mCAAmC,CAAC;AAG3C;;;;;;;;GAQG;AACH,qBAAa,sBAAuB,SAAQ,UAAU,CACpD,mBAAmB,EACnB,iBAAiB,EACjB,4BAA4B,EAC5B,wBAAwB,CACzB;IACC,SAAgB,WAAW,EAAE,MAAM,CAAiC;;IAUvD,kBAAkB,CAC7B,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,uBAAuB,EAAE,CAAC;IAuBxB,WAAW,CACtB,SAAS,EAAE,mBAAmB,EAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,iBAAiB,CAAC;IAQhB,QAAQ,CACnB,SAAS,EAAE,mBAAmB,EAC9B,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC;IAO9B,UAAU,CACrB,SAAS,EAAE,mBAAmB,EAC9B,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,IAAI,CAAC;IAIhB;;;;OAIG;IACU,eAAe,CAC1B,SAAS,EAAE,MAAM,EACjB,cAAc,UAAQ,EACtB,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,0BAA0B,EAAE,CAAC;IAWxC,iFAAiF;IACpE,kBAAkB,CAC7B,QAAQ,EAAE,0BAA0B,EAAE,EACtC,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,IAAI,CAAC;IAMhB,kGAAkG;IACrF,cAAc,CAAC,EAAE,GAAE,KAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD,oBAAoB,CAC/B,SAAS,EAAE,mBAAmB,EAC9B,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,MAAM,CAAC;CAWnB;AAGD,eAAO,MAAM,sBAAsB,8BACQ,CAAC"}
@@ -0,0 +1,90 @@
1
+ import { chargingProcessesInitializer, chargingProcessesMutator, } from "@mxpicture/tesla-superchargers-generated/public";
2
+ import { IMateStore } from "./IMateStore.js";
3
+ import { sql } from "./db.js";
4
+ /**
5
+ * Access to TeslaMate's `charging_processes` table. The helpers here
6
+ * ({@link ChargingProcessesStore.getUnpricedCharges},
7
+ * {@link ChargingProcessesStore.updatePrice}) will back the planned charge-cost
8
+ * backfill; nothing calls them yet. The backfill's guardrails, once wired up:
9
+ * only charges matched to one of our priced Superchargers, and only rows where
10
+ * `cost IS NULL` — an existing cost (TeslaMate's or the user's) is never
11
+ * overwritten.
12
+ */
13
+ export class ChargingProcessesStore extends IMateStore {
14
+ statesTable = "pic_charging_process_states";
15
+ constructor() {
16
+ super("charging_processes", chargingProcessesInitializer, chargingProcessesMutator);
17
+ }
18
+ async getUnpricedCharges(tx = sql()) {
19
+ return await tx `
20
+ SELECT
21
+ cp.id,
22
+ cp.start_date,
23
+ cp.end_date,
24
+ cp.car_id,
25
+ cp.position_id,
26
+ cp.address_id,
27
+ cp.charge_energy_added,
28
+ cp.charge_energy_used,
29
+ cp.duration_min,
30
+ p.latitude,
31
+ p.longitude,
32
+ s.id AS states_id,
33
+ s.failure_count
34
+ FROM charging_processes cp
35
+ INNER JOIN positions p ON p.id = cp.position_id
36
+ LEFT OUTER JOIN pic_charging_process_states as s ON s.charging_processes_id = cp.id
37
+ WHERE cp.cost IS NULL
38
+ AND cp.end_date IS NOT NULL;`;
39
+ }
40
+ async updatePrice(processId, cost, tx = sql()) {
41
+ return await this.update(processId, { cost: cost?.toString().replace(",", ".") ?? null }, tx);
42
+ }
43
+ async getState(processId, tx = sql()) {
44
+ const [row] = await tx `SELECT * FROM ${tx(this.statesTable)} WHERE charging_processes_id = ${processId}`;
45
+ return row ?? null;
46
+ }
47
+ async resetState(processId, tx = sql()) {
48
+ await tx `DELETE FROM ${tx(this.statesTable)} WHERE charging_processes_id = ${processId}`;
49
+ }
50
+ /**
51
+ * Failed state rows at or above `threshold`, most-failing first, each joined
52
+ * to its charge's `start_date`. With `onlyUnnotified` only rows not yet paged
53
+ * (`failure_notified_at IS NULL`) are returned.
54
+ */
55
+ async getFailedStates(threshold, onlyUnnotified = false, tx = sql()) {
56
+ return await tx `
57
+ SELECT s.*, cp.start_date
58
+ FROM ${tx(this.statesTable)} s
59
+ LEFT JOIN charging_processes cp ON cp.id = s.charging_processes_id
60
+ WHERE s.failure_count >= ${threshold}
61
+ ${onlyUnnotified ? tx `AND s.failure_notified_at IS NULL` : tx ``}
62
+ ORDER BY s.failure_count DESC
63
+ `;
64
+ }
65
+ /** Stamp the notified flag on the given state rows (by id; empty is a no-op). */
66
+ async markStatesNotified(stateIds, tx = sql()) {
67
+ await tx `UPDATE ${tx(this.statesTable)}
68
+ SET failure_notified_at = CURRENT_TIMESTAMP
69
+ WHERE id = ANY(${stateIds}::bigint[])`;
70
+ }
71
+ /** Bulk reset = delete every state row (matching single-row resetState); returns rows deleted. */
72
+ async resetAllStates(tx = sql()) {
73
+ const result = await tx `DELETE FROM ${tx(this.statesTable)}`;
74
+ return result.count;
75
+ }
76
+ async incrementStateFailed(processId, tx = sql()) {
77
+ const [row] = await tx `
78
+ INSERT INTO ${tx(this.statesTable)} (created_at, updated_at, charging_processes_id, failure_count)
79
+ VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ${processId}, 1)
80
+ ON CONFLICT (charging_processes_id)
81
+ DO UPDATE SET failure_count = ${tx(this.statesTable)}.failure_count + 1,
82
+ updated_at = CURRENT_TIMESTAMP
83
+ RETURNING failure_count
84
+ `;
85
+ return row.failure_count;
86
+ }
87
+ }
88
+ let _store = null;
89
+ export const chargingProcessesStore = () => (_store ??= new ChargingProcessesStore());
90
+ //# sourceMappingURL=ChargingProcessesStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChargingProcessesStore.js","sourceRoot":"","sources":["../../src/store/ChargingProcessesStore.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,GAOzB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAc,MAAM,SAAS,CAAC;AAO1C;;;;;;;;GAQG;AACH,MAAM,OAAO,sBAAuB,SAAQ,UAK3C;IACiB,WAAW,GAAW,6BAA6B,CAAC;IAEpE;QACE,KAAK,CACH,oBAAoB,EACpB,4BAA4B,EAC5B,wBAAwB,CACzB,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAC7B,KAAY,GAAG,EAAE;QAEjB,OAAO,MAAM,EAAE,CAA2B;;;;;;;;;;;;;;;;;;;uCAmBP,CAAC;IACtC,CAAC;IAEM,KAAK,CAAC,WAAW,CACtB,SAA8B,EAC9B,IAAmB,EACnB,KAAY,GAAG,EAAE;QAEjB,OAAO,MAAM,IAAI,CAAC,MAAM,CACtB,SAAS,EACT,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,EACpD,EAAE,CACH,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,QAAQ,CACnB,SAA8B,EAC9B,KAAY,GAAG,EAAE;QAEjB,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAErB,iBAAiB,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,kCAAkC,SAAS,EAAE,CAAC;QACpF,OAAO,GAAG,IAAI,IAAI,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,UAAU,CACrB,SAA8B,EAC9B,KAAY,GAAG,EAAE;QAEjB,MAAM,EAAE,CAAA,eAAe,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,kCAAkC,SAAS,EAAE,CAAC;IAC3F,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,eAAe,CAC1B,SAAiB,EACjB,cAAc,GAAG,KAAK,EACtB,KAAY,GAAG,EAAE;QAEjB,OAAO,MAAM,EAAE,CAA8B;;eAElC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;;mCAEA,SAAS;UAClC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAA,mCAAmC,CAAC,CAAC,CAAC,EAAE,CAAA,EAAE;;KAElE,CAAC;IACJ,CAAC;IAED,iFAAiF;IAC1E,KAAK,CAAC,kBAAkB,CAC7B,QAAsC,EACtC,KAAY,GAAG,EAAE;QAEjB,MAAM,EAAE,CAAA,UAAU,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;;uBAEnB,QAA+B,aAAa,CAAC;IAClE,CAAC;IAED,kGAAkG;IAC3F,KAAK,CAAC,cAAc,CAAC,KAAY,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAA,eAAe,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7D,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAEM,KAAK,CAAC,oBAAoB,CAC/B,SAA8B,EAC9B,KAAY,GAAG,EAAE;QAEjB,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAuB;oBAC7B,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;wDACgB,SAAS;;wCAEzB,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;;;OAGrD,CAAC;QACJ,OAAO,GAAG,CAAC,aAAa,CAAC;IAC3B,CAAC;CACF;AAED,IAAI,MAAM,GAAkC,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,EAAE,CACzC,CAAC,MAAM,KAAK,IAAI,sBAAsB,EAAE,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type Geofences, type GeofencesId, type GeofencesInitializer, type GeofencesMutator } from "@mxpicture/tesla-superchargers-generated/public";
2
+ import { IMateStore } from "./IMateStore.js";
3
+ import type { Coordinate } from "../common/coordinates.js";
4
+ import { type SqlTx } from "./db.js";
5
+ /**
6
+ * Read access to TeslaMate's `geofences` table. Today this is only
7
+ * {@link GeofencesStore.getHome}; writing priced Supercharger geofences back
8
+ * into TeslaMate is planned but not implemented yet.
9
+ */
10
+ export declare class GeofencesStore extends IMateStore<GeofencesId, Geofences, GeofencesInitializer, GeofencesMutator> {
11
+ constructor();
12
+ /**
13
+ * The coordinate of the TeslaMate geofence named `name` (case-insensitive,
14
+ * default "Home"), or null if there is none. Used to center a radius scope on
15
+ * your home without duplicating the coordinate in config.
16
+ */
17
+ getHome(geofenceName?: string, tx?: SqlTx): Promise<Coordinate | null>;
18
+ }
19
+ export declare const geofencesStore: () => GeofencesStore;
20
+ //# sourceMappingURL=GeofencesStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeofencesStore.d.ts","sourceRoot":"","sources":["../../src/store/GeofencesStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACtB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAO,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,UAAU,CAC5C,WAAW,EACX,SAAS,EACT,oBAAoB,EACpB,gBAAgB,CACjB;;IAKC;;;;OAIG;IACU,OAAO,CAClB,YAAY,GAAE,MAAe,EAC7B,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CAU9B;AAGD,eAAO,MAAM,cAAc,sBAA0C,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { geofencesInitializer, geofencesMutator, } from "@mxpicture/tesla-superchargers-generated/public";
2
+ import { IMateStore } from "./IMateStore.js";
3
+ import { sql } from "./db.js";
4
+ /**
5
+ * Read access to TeslaMate's `geofences` table. Today this is only
6
+ * {@link GeofencesStore.getHome}; writing priced Supercharger geofences back
7
+ * into TeslaMate is planned but not implemented yet.
8
+ */
9
+ export class GeofencesStore extends IMateStore {
10
+ constructor() {
11
+ super("geofences", geofencesInitializer, geofencesMutator);
12
+ }
13
+ /**
14
+ * The coordinate of the TeslaMate geofence named `name` (case-insensitive,
15
+ * default "Home"), or null if there is none. Used to center a radius scope on
16
+ * your home without duplicating the coordinate in config.
17
+ */
18
+ async getHome(geofenceName = "home", tx = sql()) {
19
+ const rows = await tx `SELECT latitude, longitude FROM geofences
20
+ WHERE name ILIKE ${geofenceName}
21
+ ORDER BY id
22
+ LIMIT 1`;
23
+ return rows[0] ?? null;
24
+ }
25
+ }
26
+ let _store = null;
27
+ export const geofencesStore = () => (_store ??= new GeofencesStore());
28
+ //# sourceMappingURL=GeofencesStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeofencesStore.js","sourceRoot":"","sources":["../../src/store/GeofencesStore.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,gBAAgB,GAKjB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,GAAG,EAAc,MAAM,SAAS,CAAC;AAE1C;;;;GAIG;AACH,MAAM,OAAO,cAAe,SAAQ,UAKnC;IACC;QACE,KAAK,CAAC,WAAW,EAAE,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;IAC7D,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,OAAO,CAClB,eAAuB,MAAM,EAC7B,KAAY,GAAG,EAAE;QAEjB,MAAM,IAAI,GAAG,MAAM,EAAE,CAEpB;2BACsB,YAAY;;gBAEvB,CAAC;QAEb,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IACzB,CAAC;CACF;AAED,IAAI,MAAM,GAA0B,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,CAAC,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type SqlTx } from "./db.js";
2
+ import { IMateStore, type MateStoreId } from "./IMateStore.js";
3
+ export interface FailableMutator {
4
+ failure_count?: number;
5
+ failure_notified_at?: Date | null;
6
+ }
7
+ export interface FailableInitializer {
8
+ failure_count: number;
9
+ }
10
+ export interface Failable<T extends number | string> extends MateStoreId<T>, FailableInitializer {
11
+ }
12
+ export declare abstract class IFailableStore<ID extends number | string, BASE extends Failable<ID>, INITIALZER extends FailableInitializer, MUTATOR extends FailableMutator> extends IMateStore<ID, BASE, INITIALZER, MUTATOR> {
13
+ protected abstract parseFailure(failure_count: number): MUTATOR;
14
+ incrementFailed(id: string, tx?: SqlTx): Promise<number>;
15
+ setFailed(id: ID, failure_count: number, tx?: SqlTx): Promise<BASE>;
16
+ /** Clears both the failure count and the notified flag, re-arming paging. */
17
+ resetFailed(id: ID, tx?: SqlTx): Promise<BASE>;
18
+ /**
19
+ * Rows at or above `threshold` failures, most-failing first. With
20
+ * `onlyUnnotified` only rows that have not been paged yet
21
+ * (`failure_notified_at IS NULL`) are returned.
22
+ */
23
+ getFailed(threshold: number, onlyUnnotified?: boolean, tx?: SqlTx): Promise<BASE[]>;
24
+ /** Stamp the notified flag on the given rows (empty list is a no-op). */
25
+ markNotified(ids: ID[], tx?: SqlTx): Promise<void>;
26
+ /** Reset every failing row (count + notified flag); returns rows affected. */
27
+ resetAllFailed(tx?: SqlTx): Promise<number>;
28
+ }
29
+ //# sourceMappingURL=IFailableStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IFailableStore.d.ts","sourceRoot":"","sources":["../../src/store/IFailableStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/D,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CACjD,SAAQ,WAAW,CAAC,CAAC,CAAC,EAAE,mBAAmB;CAAG;AAEhD,8BAAsB,cAAc,CAClC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,CAAC,EACzB,UAAU,SAAS,mBAAmB,EACtC,OAAO,SAAS,eAAe,CAC/B,SAAQ,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC;IACjD,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IAElD,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,GAAE,KAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAU/D,SAAS,CACpB,EAAE,EAAE,EAAE,EACN,aAAa,EAAE,MAAM,EACrB,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,IAAI,CAAC;IAIhB,6EAA6E;IAChE,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAE,KAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlE;;;;OAIG;IACU,SAAS,CACpB,SAAS,EAAE,MAAM,EACjB,cAAc,UAAQ,EACtB,EAAE,GAAE,KAAa,GAChB,OAAO,CAAC,IAAI,EAAE,CAAC;IASlB,yEAAyE;IAC5D,YAAY,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,GAAE,KAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtE,8EAA8E;IACjE,cAAc,CAAC,EAAE,GAAE,KAAa,GAAG,OAAO,CAAC,MAAM,CAAC;CAMhE"}
@@ -0,0 +1,47 @@
1
+ import { sql } from "./db.js";
2
+ import { IMateStore } from "./IMateStore.js";
3
+ export class IFailableStore extends IMateStore {
4
+ async incrementFailed(id, tx = sql()) {
5
+ const [row] = await tx `
6
+ UPDATE ${tx(this.table)}
7
+ SET failure_count = failure_count + 1
8
+ WHERE id = ${id}
9
+ RETURNING failure_count
10
+ `;
11
+ return row.failure_count;
12
+ }
13
+ async setFailed(id, failure_count, tx = sql()) {
14
+ return await this.update(id, this.parseFailure(failure_count), tx);
15
+ }
16
+ /** Clears both the failure count and the notified flag, re-arming paging. */
17
+ async resetFailed(id, tx = sql()) {
18
+ return await this.update(id, { ...this.parseFailure(0), failure_notified_at: null }, tx);
19
+ }
20
+ /**
21
+ * Rows at or above `threshold` failures, most-failing first. With
22
+ * `onlyUnnotified` only rows that have not been paged yet
23
+ * (`failure_notified_at IS NULL`) are returned.
24
+ */
25
+ async getFailed(threshold, onlyUnnotified = false, tx = sql()) {
26
+ return await tx `
27
+ SELECT * FROM ${tx(this.table)}
28
+ WHERE failure_count >= ${threshold}
29
+ ${onlyUnnotified ? tx `AND failure_notified_at IS NULL` : tx ``}
30
+ ORDER BY failure_count DESC
31
+ `;
32
+ }
33
+ /** Stamp the notified flag on the given rows (empty list is a no-op). */
34
+ async markNotified(ids, tx = sql()) {
35
+ await tx `UPDATE ${tx(this.table)}
36
+ SET failure_notified_at = CURRENT_TIMESTAMP
37
+ WHERE id = ANY(${ids}::bigint[])`;
38
+ }
39
+ /** Reset every failing row (count + notified flag); returns rows affected. */
40
+ async resetAllFailed(tx = sql()) {
41
+ const result = await tx `UPDATE ${tx(this.table)}
42
+ SET failure_count = 0, failure_notified_at = NULL
43
+ WHERE failure_count > 0`;
44
+ return result.count;
45
+ }
46
+ }
47
+ //# sourceMappingURL=IFailableStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IFailableStore.js","sourceRoot":"","sources":["../../src/store/IFailableStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAc,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAoB,MAAM,iBAAiB,CAAC;AAc/D,MAAM,OAAgB,cAKpB,SAAQ,UAAyC;IAG1C,KAAK,CAAC,eAAe,CAAC,EAAU,EAAE,KAAY,GAAG,EAAE;QACxD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAuB;eAClC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;;qBAER,EAAE;;KAElB,CAAC;QACF,OAAO,GAAG,CAAC,aAAa,CAAC;IAC3B,CAAC;IAEM,KAAK,CAAC,SAAS,CACpB,EAAM,EACN,aAAqB,EACrB,KAAY,GAAG,EAAE;QAEjB,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,6EAA6E;IACtE,KAAK,CAAC,WAAW,CAAC,EAAM,EAAE,KAAY,GAAG,EAAE;QAChD,OAAO,MAAM,IAAI,CAAC,MAAM,CACtB,EAAE,EACF,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,EACtD,EAAE,CACH,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,SAAS,CACpB,SAAiB,EACjB,cAAc,GAAG,KAAK,EACtB,KAAY,GAAG,EAAE;QAEjB,OAAO,MAAM,EAAE,CAAQ;sBACL,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;iCACH,SAAS;UAChC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAA,iCAAiC,CAAC,CAAC,CAAC,EAAE,CAAA,EAAE;;KAEhE,CAAC;IACJ,CAAC;IAED,yEAAyE;IAClE,KAAK,CAAC,YAAY,CAAC,GAAS,EAAE,KAAY,GAAG,EAAE;QACpD,MAAM,EAAE,CAAA,UAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;;uBAEb,GAA0B,aAAa,CAAC;IAC7D,CAAC;IAED,8EAA8E;IACvE,KAAK,CAAC,cAAc,CAAC,KAAY,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAA,UAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;;8BAErB,CAAC;QAC3B,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;CACF"}
@@ -0,0 +1,66 @@
1
+ import type { z } from "zod";
2
+ import { type SqlTx } from "./db.js";
3
+ import type { Meta } from "../types/client.types.js";
4
+ /**
5
+ * Run `fn` inside one transaction, serialized per `lockKey` via a Postgres
6
+ * advisory lock — concurrent runs on the same key wait instead of interleaving.
7
+ */
8
+ export declare const transaction: <T>(lockKey: string, fn: (tx: SqlTx) => Promise<T>) => Promise<T>;
9
+ export interface MateStoreId<T extends number | string> {
10
+ id: T;
11
+ }
12
+ export interface FoundCoordinate<T> {
13
+ entity: T;
14
+ distanceInKm: number;
15
+ }
16
+ export declare abstract class IMateStore<ID extends number | string, BASE extends MateStoreId<ID>, INITIALZER extends object, MUTATOR extends object> {
17
+ readonly table: string;
18
+ protected readonly initializer: z.Schema<INITIALZER>;
19
+ protected readonly mutator: z.Schema<MUTATOR>;
20
+ readonly view: string | null;
21
+ constructor(table: string, initializer: z.Schema<INITIALZER>, mutator: z.Schema<MUTATOR>, view?: string | null);
22
+ createTable(tx?: SqlTx): Promise<void>;
23
+ get(id: ID, tx?: SqlTx): Promise<BASE | null>;
24
+ query(filters?: Partial<BASE>, tx?: SqlTx): Promise<BASE[]>;
25
+ delete(id: ID, tx?: SqlTx): Promise<void>;
26
+ deleteAll(tx?: SqlTx): Promise<void>;
27
+ insert(data: INITIALZER, tx?: SqlTx): Promise<BASE>;
28
+ insertMany(data: INITIALZER[], tx?: SqlTx): Promise<number>;
29
+ /**
30
+ * Single-row `INSERT … ON CONFLICT (conflictColumn) DO UPDATE … RETURNING *`.
31
+ * Unlike `upsertMany` the update is unconditional, so the meta columns
32
+ * (`fetched_at`, `last_fetch_uuid`) advance even when the content is
33
+ * unchanged — callers rely on that for staleness tracking.
34
+ */
35
+ upsertOne(data: INITIALZER, opts: {
36
+ conflictColumn: string;
37
+ /** columns SET from EXCLUDED on conflict */
38
+ updateColumns: string[];
39
+ }, tx?: SqlTx): Promise<BASE>;
40
+ /**
41
+ * Chunked `INSERT … ON CONFLICT (conflictColumn) DO UPDATE`. A conflicting
42
+ * row is only written when its `compareColumns` differ by value
43
+ * (`IS DISTINCT FROM`) from the incoming row; untouched rows are not part
44
+ * of the returned count.
45
+ */
46
+ upsertMany(data: INITIALZER[], opts: {
47
+ conflictColumn: string;
48
+ /** columns SET from EXCLUDED when the row changed */
49
+ updateColumns: string[];
50
+ /** columns compared to decide whether the row changed */
51
+ compareColumns: string[];
52
+ }, tx?: SqlTx): Promise<number>;
53
+ /**
54
+ * Soft-delete every not-yet-deleted row whose id is NOT in `ids` — i.e. the
55
+ * rows absent from the current snapshot. Assumes the table's meta columns
56
+ * (`deleted_at`, `fetched_at`, `last_fetch_uuid`) and a bigint/int identity id.
57
+ */
58
+ softDeleteWhereIdNotIn(ids: ID[], meta: Meta, tx?: SqlTx): Promise<number>;
59
+ /**
60
+ * Hard-delete every row whose id is NOT in `ids` — for tables that mirror
61
+ * their feed instead of soft-deleting absent rows.
62
+ */
63
+ deleteWhereIdNotIn(ids: ID[], tx?: SqlTx): Promise<number>;
64
+ update(id: ID, data: MUTATOR, tx?: SqlTx): Promise<BASE>;
65
+ }
66
+ //# sourceMappingURL=IMateStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IMateStore.d.ts","sourceRoot":"","sources":["../../src/store/IMateStore.ts"],"names":[],"mappings":"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"}