@findatruck/convex-client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +210 -0
- package/dist/index.d.cts +154 -0
- package/dist/index.d.ts +154 -0
- package/dist/index.js +182 -0
- package/package.json +38 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
BatchConvexUpdateSchema: () => import_shared_schemas2.BatchConvexUpdateSchema,
|
|
24
|
+
ConvexClient: () => ConvexClient,
|
|
25
|
+
ConvexClientError: () => ConvexClientError,
|
|
26
|
+
ConvexDriverSchema: () => import_shared_schemas2.ConvexDriverSchema,
|
|
27
|
+
ConvexUpdateSchema: () => import_shared_schemas2.ConvexUpdateSchema,
|
|
28
|
+
ScrapeStatus: () => import_shared_schemas2.ScrapeStatus,
|
|
29
|
+
UpdateScrapeStatusMessage: () => import_shared_schemas2.UpdateScrapeStatusMessage,
|
|
30
|
+
checkWorkerSecret: () => checkWorkerSecret,
|
|
31
|
+
parseAndValidate: () => parseAndValidate,
|
|
32
|
+
responses: () => responses,
|
|
33
|
+
routes: () => routes
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/routes.ts
|
|
38
|
+
var import_shared_schemas = require("@findatruck/shared-schemas");
|
|
39
|
+
var routes = {
|
|
40
|
+
ingestDriverView: {
|
|
41
|
+
path: "/ingest/driver-view",
|
|
42
|
+
method: "POST",
|
|
43
|
+
requestSchema: import_shared_schemas.ConvexUpdateSchema
|
|
44
|
+
},
|
|
45
|
+
ingestProviderAccountStatus: {
|
|
46
|
+
path: "/ingest/provider-account-status",
|
|
47
|
+
method: "POST",
|
|
48
|
+
requestSchema: import_shared_schemas.UpdateScrapeStatusMessage
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/types.ts
|
|
53
|
+
var ConvexClientError = class extends Error {
|
|
54
|
+
constructor(message, endpoint, status, responseBody) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.endpoint = endpoint;
|
|
57
|
+
this.status = status;
|
|
58
|
+
this.responseBody = responseBody;
|
|
59
|
+
this.name = "ConvexClientError";
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/client.ts
|
|
64
|
+
var ConvexClient = class {
|
|
65
|
+
constructor(config) {
|
|
66
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
67
|
+
this.workerSecret = config.workerSecret;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Posts driver view data to the Convex ingestion endpoint.
|
|
71
|
+
*/
|
|
72
|
+
async postDriverView(data) {
|
|
73
|
+
const route = routes.ingestDriverView;
|
|
74
|
+
const parseResult = route.requestSchema.safeParse(data);
|
|
75
|
+
if (!parseResult.success) {
|
|
76
|
+
throw new ConvexClientError(
|
|
77
|
+
`Invalid request: ${parseResult.error.message}`,
|
|
78
|
+
route.path
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
await this.makeRequest(route.path, route.method, data);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Posts provider account status updates to the Convex ingestion endpoint.
|
|
85
|
+
*/
|
|
86
|
+
async postProviderAccountStatus(data) {
|
|
87
|
+
const route = routes.ingestProviderAccountStatus;
|
|
88
|
+
const parseResult = route.requestSchema.safeParse(data);
|
|
89
|
+
if (!parseResult.success) {
|
|
90
|
+
throw new ConvexClientError(
|
|
91
|
+
`Invalid request: ${parseResult.error.message}`,
|
|
92
|
+
route.path
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
await this.makeRequest(route.path, route.method, data);
|
|
96
|
+
}
|
|
97
|
+
async makeRequest(path, method, body) {
|
|
98
|
+
const url = `${this.baseUrl}${path}`;
|
|
99
|
+
const response = await fetch(url, {
|
|
100
|
+
method,
|
|
101
|
+
headers: {
|
|
102
|
+
"Content-Type": "application/json",
|
|
103
|
+
"Authorization": `Bearer ${this.workerSecret}`
|
|
104
|
+
},
|
|
105
|
+
body: JSON.stringify(body)
|
|
106
|
+
});
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
let responseBody;
|
|
109
|
+
try {
|
|
110
|
+
responseBody = await response.json();
|
|
111
|
+
} catch {
|
|
112
|
+
responseBody = await response.text();
|
|
113
|
+
}
|
|
114
|
+
throw new ConvexClientError(
|
|
115
|
+
`Request to ${path} failed with status ${response.status}`,
|
|
116
|
+
path,
|
|
117
|
+
response.status,
|
|
118
|
+
responseBody
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// src/validation.ts
|
|
125
|
+
function checkWorkerSecret(req, expectedSecret) {
|
|
126
|
+
const authHeader = req.headers.get("Authorization");
|
|
127
|
+
if (!authHeader) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const expectedValue = `Bearer ${expectedSecret}`;
|
|
131
|
+
if (authHeader.length !== expectedValue.length) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
let result = 0;
|
|
135
|
+
for (let i = 0; i < authHeader.length; i++) {
|
|
136
|
+
result |= authHeader.charCodeAt(i) ^ expectedValue.charCodeAt(i);
|
|
137
|
+
}
|
|
138
|
+
return result === 0;
|
|
139
|
+
}
|
|
140
|
+
async function parseAndValidate(schema, req) {
|
|
141
|
+
let body;
|
|
142
|
+
try {
|
|
143
|
+
body = await req.json();
|
|
144
|
+
} catch {
|
|
145
|
+
return { success: false, error: "Invalid JSON body" };
|
|
146
|
+
}
|
|
147
|
+
const result = schema.safeParse(body);
|
|
148
|
+
if (!result.success) {
|
|
149
|
+
return { success: false, error: result.error.message };
|
|
150
|
+
}
|
|
151
|
+
return { success: true, data: result.data };
|
|
152
|
+
}
|
|
153
|
+
var responses = {
|
|
154
|
+
/**
|
|
155
|
+
* Returns a 200 OK response with optional JSON body
|
|
156
|
+
*/
|
|
157
|
+
ok(body) {
|
|
158
|
+
if (body === void 0) {
|
|
159
|
+
return new Response(null, { status: 200 });
|
|
160
|
+
}
|
|
161
|
+
return new Response(JSON.stringify(body), {
|
|
162
|
+
status: 200,
|
|
163
|
+
headers: { "Content-Type": "application/json" }
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
/**
|
|
167
|
+
* Returns a 401 Unauthorized response
|
|
168
|
+
*/
|
|
169
|
+
unauthorized() {
|
|
170
|
+
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
171
|
+
status: 401,
|
|
172
|
+
headers: { "Content-Type": "application/json" }
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
/**
|
|
176
|
+
* Returns a 400 Bad Request response with error message
|
|
177
|
+
*/
|
|
178
|
+
badRequest(error) {
|
|
179
|
+
return new Response(JSON.stringify({ error }), {
|
|
180
|
+
status: 400,
|
|
181
|
+
headers: { "Content-Type": "application/json" }
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
/**
|
|
185
|
+
* Returns a 500 Internal Server Error response with optional error message
|
|
186
|
+
*/
|
|
187
|
+
serverError(error) {
|
|
188
|
+
return new Response(JSON.stringify({ error: error ?? "Internal server error" }), {
|
|
189
|
+
status: 500,
|
|
190
|
+
headers: { "Content-Type": "application/json" }
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// src/index.ts
|
|
196
|
+
var import_shared_schemas2 = require("@findatruck/shared-schemas");
|
|
197
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
198
|
+
0 && (module.exports = {
|
|
199
|
+
BatchConvexUpdateSchema,
|
|
200
|
+
ConvexClient,
|
|
201
|
+
ConvexClientError,
|
|
202
|
+
ConvexDriverSchema,
|
|
203
|
+
ConvexUpdateSchema,
|
|
204
|
+
ScrapeStatus,
|
|
205
|
+
UpdateScrapeStatusMessage,
|
|
206
|
+
checkWorkerSecret,
|
|
207
|
+
parseAndValidate,
|
|
208
|
+
responses,
|
|
209
|
+
routes
|
|
210
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import * as _findatruck_shared_schemas from '@findatruck/shared-schemas';
|
|
2
|
+
import { ConvexUpdateData, UpdateScrapeStatusMessage } from '@findatruck/shared-schemas';
|
|
3
|
+
export { BatchConvexUpdateData, BatchConvexUpdateSchema, ConvexDriverData, ConvexDriverSchema, ConvexUpdateData, ConvexUpdateSchema, ScrapeStatus, UpdateScrapeStatusMessage, UpdateScrapeStatusMessage as UpdateScrapeStatusMessageType } from '@findatruck/shared-schemas';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for the ConvexClient
|
|
8
|
+
*/
|
|
9
|
+
interface ConvexClientConfig {
|
|
10
|
+
/** Base URL of the Convex HTTP endpoint (e.g., "https://your-deployment.convex.site") */
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
/** Worker shared secret for authentication */
|
|
13
|
+
workerSecret: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown by the ConvexClient when requests fail
|
|
17
|
+
*/
|
|
18
|
+
declare class ConvexClientError extends Error {
|
|
19
|
+
readonly endpoint: string;
|
|
20
|
+
readonly status?: number | undefined;
|
|
21
|
+
readonly responseBody?: unknown | undefined;
|
|
22
|
+
constructor(message: string, endpoint: string, status?: number | undefined, responseBody?: unknown | undefined);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Result of parsing and validating a request body
|
|
26
|
+
*/
|
|
27
|
+
type ValidationResult<T> = {
|
|
28
|
+
success: true;
|
|
29
|
+
data: T;
|
|
30
|
+
} | {
|
|
31
|
+
success: false;
|
|
32
|
+
error: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Typed HTTP client for calling Convex HTTP endpoints from eld-aggregator.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const client = new ConvexClient({
|
|
41
|
+
* baseUrl: process.env.CONVEX_HTTP_URL,
|
|
42
|
+
* workerSecret: process.env.WORKER_SHARED_SECRET,
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* await client.postDriverView(convexUpdate);
|
|
46
|
+
* await client.postProviderAccountStatus(statusMessage);
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare class ConvexClient {
|
|
50
|
+
private readonly baseUrl;
|
|
51
|
+
private readonly workerSecret;
|
|
52
|
+
constructor(config: ConvexClientConfig);
|
|
53
|
+
/**
|
|
54
|
+
* Posts driver view data to the Convex ingestion endpoint.
|
|
55
|
+
*/
|
|
56
|
+
postDriverView(data: ConvexUpdateData): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Posts provider account status updates to the Convex ingestion endpoint.
|
|
59
|
+
*/
|
|
60
|
+
postProviderAccountStatus(data: UpdateScrapeStatusMessage): Promise<void>;
|
|
61
|
+
private makeRequest;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Route definition with path, method, and request schema
|
|
66
|
+
*/
|
|
67
|
+
interface RouteDefinition<TRequest extends z.ZodType> {
|
|
68
|
+
path: string;
|
|
69
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
70
|
+
requestSchema: TRequest;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Single source of truth for all Convex HTTP routes
|
|
74
|
+
* consumed from eld-aggregator and findatruck-next Convex backend.
|
|
75
|
+
*/
|
|
76
|
+
declare const routes: {
|
|
77
|
+
readonly ingestDriverView: {
|
|
78
|
+
readonly path: "/ingest/driver-view";
|
|
79
|
+
readonly method: "POST";
|
|
80
|
+
readonly requestSchema: z.ZodObject<{
|
|
81
|
+
provider_url: z.ZodString;
|
|
82
|
+
username: z.ZodString;
|
|
83
|
+
drivers: z.ZodArray<z.ZodObject<{
|
|
84
|
+
driver_name: z.ZodOptional<z.ZodString>;
|
|
85
|
+
vehicle_id: z.ZodOptional<z.ZodString>;
|
|
86
|
+
driver_status: z.ZodString;
|
|
87
|
+
time_remaining_in_shift: z.ZodNumber;
|
|
88
|
+
time_remaining_till_break: z.ZodNumber;
|
|
89
|
+
time_remaining_in_week: z.ZodNumber;
|
|
90
|
+
time_remaining_in_drive: z.ZodNumber;
|
|
91
|
+
driver_current_location_latitude: z.ZodNumber;
|
|
92
|
+
driver_current_location_longitude: z.ZodNumber;
|
|
93
|
+
driver_current_location_address: z.ZodString;
|
|
94
|
+
license_number: z.ZodOptional<z.ZodString>;
|
|
95
|
+
license_state: z.ZodOptional<z.ZodString>;
|
|
96
|
+
speed: z.ZodOptional<z.ZodNumber>;
|
|
97
|
+
odometer: z.ZodOptional<z.ZodNumber>;
|
|
98
|
+
convex_provider_account_id: z.ZodString;
|
|
99
|
+
external_provider_account_id: z.ZodString;
|
|
100
|
+
external_driver_id: z.ZodString;
|
|
101
|
+
mileage_since_last_update: z.ZodOptional<z.ZodNumber>;
|
|
102
|
+
}, z.core.$strip>>;
|
|
103
|
+
version: z.ZodDefault<z.ZodString>;
|
|
104
|
+
}, z.core.$strip>;
|
|
105
|
+
};
|
|
106
|
+
readonly ingestProviderAccountStatus: {
|
|
107
|
+
readonly path: "/ingest/provider-account-status";
|
|
108
|
+
readonly method: "POST";
|
|
109
|
+
readonly requestSchema: z.ZodObject<{
|
|
110
|
+
status: z.ZodEnum<typeof _findatruck_shared_schemas.ScrapeStatus>;
|
|
111
|
+
correctPassword: z.ZodOptional<z.ZodBoolean>;
|
|
112
|
+
externalProviderAccountId: z.ZodString;
|
|
113
|
+
username: z.ZodString;
|
|
114
|
+
provider_url: z.ZodString;
|
|
115
|
+
driverCount: z.ZodOptional<z.ZodNumber>;
|
|
116
|
+
providerSlug: z.ZodOptional<z.ZodString>;
|
|
117
|
+
}, z.core.$strip>;
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
type RouteName = keyof typeof routes;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Constant-time string comparison to prevent timing attacks.
|
|
124
|
+
* Returns true if the Authorization header matches the expected Bearer token.
|
|
125
|
+
*/
|
|
126
|
+
declare function checkWorkerSecret(req: Request, expectedSecret: string): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Parses the JSON body of a request and validates it against a Zod schema.
|
|
129
|
+
* Returns a ValidationResult with either the parsed data or an error message.
|
|
130
|
+
*/
|
|
131
|
+
declare function parseAndValidate<T>(schema: z.ZodType<T>, req: Request): Promise<ValidationResult<T>>;
|
|
132
|
+
/**
|
|
133
|
+
* Standard HTTP responses for Convex httpAction handlers.
|
|
134
|
+
*/
|
|
135
|
+
declare const responses: {
|
|
136
|
+
/**
|
|
137
|
+
* Returns a 200 OK response with optional JSON body
|
|
138
|
+
*/
|
|
139
|
+
readonly ok: (body?: unknown) => Response;
|
|
140
|
+
/**
|
|
141
|
+
* Returns a 401 Unauthorized response
|
|
142
|
+
*/
|
|
143
|
+
readonly unauthorized: () => Response;
|
|
144
|
+
/**
|
|
145
|
+
* Returns a 400 Bad Request response with error message
|
|
146
|
+
*/
|
|
147
|
+
readonly badRequest: (error: string) => Response;
|
|
148
|
+
/**
|
|
149
|
+
* Returns a 500 Internal Server Error response with optional error message
|
|
150
|
+
*/
|
|
151
|
+
readonly serverError: (error?: string) => Response;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export { ConvexClient, type ConvexClientConfig, ConvexClientError, type RouteDefinition, type RouteName, type ValidationResult, checkWorkerSecret, parseAndValidate, responses, routes };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import * as _findatruck_shared_schemas from '@findatruck/shared-schemas';
|
|
2
|
+
import { ConvexUpdateData, UpdateScrapeStatusMessage } from '@findatruck/shared-schemas';
|
|
3
|
+
export { BatchConvexUpdateData, BatchConvexUpdateSchema, ConvexDriverData, ConvexDriverSchema, ConvexUpdateData, ConvexUpdateSchema, ScrapeStatus, UpdateScrapeStatusMessage, UpdateScrapeStatusMessage as UpdateScrapeStatusMessageType } from '@findatruck/shared-schemas';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for the ConvexClient
|
|
8
|
+
*/
|
|
9
|
+
interface ConvexClientConfig {
|
|
10
|
+
/** Base URL of the Convex HTTP endpoint (e.g., "https://your-deployment.convex.site") */
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
/** Worker shared secret for authentication */
|
|
13
|
+
workerSecret: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown by the ConvexClient when requests fail
|
|
17
|
+
*/
|
|
18
|
+
declare class ConvexClientError extends Error {
|
|
19
|
+
readonly endpoint: string;
|
|
20
|
+
readonly status?: number | undefined;
|
|
21
|
+
readonly responseBody?: unknown | undefined;
|
|
22
|
+
constructor(message: string, endpoint: string, status?: number | undefined, responseBody?: unknown | undefined);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Result of parsing and validating a request body
|
|
26
|
+
*/
|
|
27
|
+
type ValidationResult<T> = {
|
|
28
|
+
success: true;
|
|
29
|
+
data: T;
|
|
30
|
+
} | {
|
|
31
|
+
success: false;
|
|
32
|
+
error: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Typed HTTP client for calling Convex HTTP endpoints from eld-aggregator.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const client = new ConvexClient({
|
|
41
|
+
* baseUrl: process.env.CONVEX_HTTP_URL,
|
|
42
|
+
* workerSecret: process.env.WORKER_SHARED_SECRET,
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* await client.postDriverView(convexUpdate);
|
|
46
|
+
* await client.postProviderAccountStatus(statusMessage);
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare class ConvexClient {
|
|
50
|
+
private readonly baseUrl;
|
|
51
|
+
private readonly workerSecret;
|
|
52
|
+
constructor(config: ConvexClientConfig);
|
|
53
|
+
/**
|
|
54
|
+
* Posts driver view data to the Convex ingestion endpoint.
|
|
55
|
+
*/
|
|
56
|
+
postDriverView(data: ConvexUpdateData): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Posts provider account status updates to the Convex ingestion endpoint.
|
|
59
|
+
*/
|
|
60
|
+
postProviderAccountStatus(data: UpdateScrapeStatusMessage): Promise<void>;
|
|
61
|
+
private makeRequest;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Route definition with path, method, and request schema
|
|
66
|
+
*/
|
|
67
|
+
interface RouteDefinition<TRequest extends z.ZodType> {
|
|
68
|
+
path: string;
|
|
69
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
70
|
+
requestSchema: TRequest;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Single source of truth for all Convex HTTP routes
|
|
74
|
+
* consumed from eld-aggregator and findatruck-next Convex backend.
|
|
75
|
+
*/
|
|
76
|
+
declare const routes: {
|
|
77
|
+
readonly ingestDriverView: {
|
|
78
|
+
readonly path: "/ingest/driver-view";
|
|
79
|
+
readonly method: "POST";
|
|
80
|
+
readonly requestSchema: z.ZodObject<{
|
|
81
|
+
provider_url: z.ZodString;
|
|
82
|
+
username: z.ZodString;
|
|
83
|
+
drivers: z.ZodArray<z.ZodObject<{
|
|
84
|
+
driver_name: z.ZodOptional<z.ZodString>;
|
|
85
|
+
vehicle_id: z.ZodOptional<z.ZodString>;
|
|
86
|
+
driver_status: z.ZodString;
|
|
87
|
+
time_remaining_in_shift: z.ZodNumber;
|
|
88
|
+
time_remaining_till_break: z.ZodNumber;
|
|
89
|
+
time_remaining_in_week: z.ZodNumber;
|
|
90
|
+
time_remaining_in_drive: z.ZodNumber;
|
|
91
|
+
driver_current_location_latitude: z.ZodNumber;
|
|
92
|
+
driver_current_location_longitude: z.ZodNumber;
|
|
93
|
+
driver_current_location_address: z.ZodString;
|
|
94
|
+
license_number: z.ZodOptional<z.ZodString>;
|
|
95
|
+
license_state: z.ZodOptional<z.ZodString>;
|
|
96
|
+
speed: z.ZodOptional<z.ZodNumber>;
|
|
97
|
+
odometer: z.ZodOptional<z.ZodNumber>;
|
|
98
|
+
convex_provider_account_id: z.ZodString;
|
|
99
|
+
external_provider_account_id: z.ZodString;
|
|
100
|
+
external_driver_id: z.ZodString;
|
|
101
|
+
mileage_since_last_update: z.ZodOptional<z.ZodNumber>;
|
|
102
|
+
}, z.core.$strip>>;
|
|
103
|
+
version: z.ZodDefault<z.ZodString>;
|
|
104
|
+
}, z.core.$strip>;
|
|
105
|
+
};
|
|
106
|
+
readonly ingestProviderAccountStatus: {
|
|
107
|
+
readonly path: "/ingest/provider-account-status";
|
|
108
|
+
readonly method: "POST";
|
|
109
|
+
readonly requestSchema: z.ZodObject<{
|
|
110
|
+
status: z.ZodEnum<typeof _findatruck_shared_schemas.ScrapeStatus>;
|
|
111
|
+
correctPassword: z.ZodOptional<z.ZodBoolean>;
|
|
112
|
+
externalProviderAccountId: z.ZodString;
|
|
113
|
+
username: z.ZodString;
|
|
114
|
+
provider_url: z.ZodString;
|
|
115
|
+
driverCount: z.ZodOptional<z.ZodNumber>;
|
|
116
|
+
providerSlug: z.ZodOptional<z.ZodString>;
|
|
117
|
+
}, z.core.$strip>;
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
type RouteName = keyof typeof routes;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Constant-time string comparison to prevent timing attacks.
|
|
124
|
+
* Returns true if the Authorization header matches the expected Bearer token.
|
|
125
|
+
*/
|
|
126
|
+
declare function checkWorkerSecret(req: Request, expectedSecret: string): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Parses the JSON body of a request and validates it against a Zod schema.
|
|
129
|
+
* Returns a ValidationResult with either the parsed data or an error message.
|
|
130
|
+
*/
|
|
131
|
+
declare function parseAndValidate<T>(schema: z.ZodType<T>, req: Request): Promise<ValidationResult<T>>;
|
|
132
|
+
/**
|
|
133
|
+
* Standard HTTP responses for Convex httpAction handlers.
|
|
134
|
+
*/
|
|
135
|
+
declare const responses: {
|
|
136
|
+
/**
|
|
137
|
+
* Returns a 200 OK response with optional JSON body
|
|
138
|
+
*/
|
|
139
|
+
readonly ok: (body?: unknown) => Response;
|
|
140
|
+
/**
|
|
141
|
+
* Returns a 401 Unauthorized response
|
|
142
|
+
*/
|
|
143
|
+
readonly unauthorized: () => Response;
|
|
144
|
+
/**
|
|
145
|
+
* Returns a 400 Bad Request response with error message
|
|
146
|
+
*/
|
|
147
|
+
readonly badRequest: (error: string) => Response;
|
|
148
|
+
/**
|
|
149
|
+
* Returns a 500 Internal Server Error response with optional error message
|
|
150
|
+
*/
|
|
151
|
+
readonly serverError: (error?: string) => Response;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export { ConvexClient, type ConvexClientConfig, ConvexClientError, type RouteDefinition, type RouteName, type ValidationResult, checkWorkerSecret, parseAndValidate, responses, routes };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// src/routes.ts
|
|
2
|
+
import {
|
|
3
|
+
ConvexUpdateSchema,
|
|
4
|
+
UpdateScrapeStatusMessage
|
|
5
|
+
} from "@findatruck/shared-schemas";
|
|
6
|
+
var routes = {
|
|
7
|
+
ingestDriverView: {
|
|
8
|
+
path: "/ingest/driver-view",
|
|
9
|
+
method: "POST",
|
|
10
|
+
requestSchema: ConvexUpdateSchema
|
|
11
|
+
},
|
|
12
|
+
ingestProviderAccountStatus: {
|
|
13
|
+
path: "/ingest/provider-account-status",
|
|
14
|
+
method: "POST",
|
|
15
|
+
requestSchema: UpdateScrapeStatusMessage
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/types.ts
|
|
20
|
+
var ConvexClientError = class extends Error {
|
|
21
|
+
constructor(message, endpoint, status, responseBody) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.endpoint = endpoint;
|
|
24
|
+
this.status = status;
|
|
25
|
+
this.responseBody = responseBody;
|
|
26
|
+
this.name = "ConvexClientError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/client.ts
|
|
31
|
+
var ConvexClient = class {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
34
|
+
this.workerSecret = config.workerSecret;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Posts driver view data to the Convex ingestion endpoint.
|
|
38
|
+
*/
|
|
39
|
+
async postDriverView(data) {
|
|
40
|
+
const route = routes.ingestDriverView;
|
|
41
|
+
const parseResult = route.requestSchema.safeParse(data);
|
|
42
|
+
if (!parseResult.success) {
|
|
43
|
+
throw new ConvexClientError(
|
|
44
|
+
`Invalid request: ${parseResult.error.message}`,
|
|
45
|
+
route.path
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
await this.makeRequest(route.path, route.method, data);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Posts provider account status updates to the Convex ingestion endpoint.
|
|
52
|
+
*/
|
|
53
|
+
async postProviderAccountStatus(data) {
|
|
54
|
+
const route = routes.ingestProviderAccountStatus;
|
|
55
|
+
const parseResult = route.requestSchema.safeParse(data);
|
|
56
|
+
if (!parseResult.success) {
|
|
57
|
+
throw new ConvexClientError(
|
|
58
|
+
`Invalid request: ${parseResult.error.message}`,
|
|
59
|
+
route.path
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
await this.makeRequest(route.path, route.method, data);
|
|
63
|
+
}
|
|
64
|
+
async makeRequest(path, method, body) {
|
|
65
|
+
const url = `${this.baseUrl}${path}`;
|
|
66
|
+
const response = await fetch(url, {
|
|
67
|
+
method,
|
|
68
|
+
headers: {
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
"Authorization": `Bearer ${this.workerSecret}`
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify(body)
|
|
73
|
+
});
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
let responseBody;
|
|
76
|
+
try {
|
|
77
|
+
responseBody = await response.json();
|
|
78
|
+
} catch {
|
|
79
|
+
responseBody = await response.text();
|
|
80
|
+
}
|
|
81
|
+
throw new ConvexClientError(
|
|
82
|
+
`Request to ${path} failed with status ${response.status}`,
|
|
83
|
+
path,
|
|
84
|
+
response.status,
|
|
85
|
+
responseBody
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/validation.ts
|
|
92
|
+
function checkWorkerSecret(req, expectedSecret) {
|
|
93
|
+
const authHeader = req.headers.get("Authorization");
|
|
94
|
+
if (!authHeader) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const expectedValue = `Bearer ${expectedSecret}`;
|
|
98
|
+
if (authHeader.length !== expectedValue.length) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
let result = 0;
|
|
102
|
+
for (let i = 0; i < authHeader.length; i++) {
|
|
103
|
+
result |= authHeader.charCodeAt(i) ^ expectedValue.charCodeAt(i);
|
|
104
|
+
}
|
|
105
|
+
return result === 0;
|
|
106
|
+
}
|
|
107
|
+
async function parseAndValidate(schema, req) {
|
|
108
|
+
let body;
|
|
109
|
+
try {
|
|
110
|
+
body = await req.json();
|
|
111
|
+
} catch {
|
|
112
|
+
return { success: false, error: "Invalid JSON body" };
|
|
113
|
+
}
|
|
114
|
+
const result = schema.safeParse(body);
|
|
115
|
+
if (!result.success) {
|
|
116
|
+
return { success: false, error: result.error.message };
|
|
117
|
+
}
|
|
118
|
+
return { success: true, data: result.data };
|
|
119
|
+
}
|
|
120
|
+
var responses = {
|
|
121
|
+
/**
|
|
122
|
+
* Returns a 200 OK response with optional JSON body
|
|
123
|
+
*/
|
|
124
|
+
ok(body) {
|
|
125
|
+
if (body === void 0) {
|
|
126
|
+
return new Response(null, { status: 200 });
|
|
127
|
+
}
|
|
128
|
+
return new Response(JSON.stringify(body), {
|
|
129
|
+
status: 200,
|
|
130
|
+
headers: { "Content-Type": "application/json" }
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
/**
|
|
134
|
+
* Returns a 401 Unauthorized response
|
|
135
|
+
*/
|
|
136
|
+
unauthorized() {
|
|
137
|
+
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
138
|
+
status: 401,
|
|
139
|
+
headers: { "Content-Type": "application/json" }
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
/**
|
|
143
|
+
* Returns a 400 Bad Request response with error message
|
|
144
|
+
*/
|
|
145
|
+
badRequest(error) {
|
|
146
|
+
return new Response(JSON.stringify({ error }), {
|
|
147
|
+
status: 400,
|
|
148
|
+
headers: { "Content-Type": "application/json" }
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
/**
|
|
152
|
+
* Returns a 500 Internal Server Error response with optional error message
|
|
153
|
+
*/
|
|
154
|
+
serverError(error) {
|
|
155
|
+
return new Response(JSON.stringify({ error: error ?? "Internal server error" }), {
|
|
156
|
+
status: 500,
|
|
157
|
+
headers: { "Content-Type": "application/json" }
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/index.ts
|
|
163
|
+
import {
|
|
164
|
+
ConvexUpdateSchema as ConvexUpdateSchema2,
|
|
165
|
+
ConvexDriverSchema,
|
|
166
|
+
BatchConvexUpdateSchema,
|
|
167
|
+
UpdateScrapeStatusMessage as UpdateScrapeStatusMessage2,
|
|
168
|
+
ScrapeStatus
|
|
169
|
+
} from "@findatruck/shared-schemas";
|
|
170
|
+
export {
|
|
171
|
+
BatchConvexUpdateSchema,
|
|
172
|
+
ConvexClient,
|
|
173
|
+
ConvexClientError,
|
|
174
|
+
ConvexDriverSchema,
|
|
175
|
+
ConvexUpdateSchema2 as ConvexUpdateSchema,
|
|
176
|
+
ScrapeStatus,
|
|
177
|
+
UpdateScrapeStatusMessage2 as UpdateScrapeStatusMessage,
|
|
178
|
+
checkWorkerSecret,
|
|
179
|
+
parseAndValidate,
|
|
180
|
+
responses,
|
|
181
|
+
routes
|
|
182
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@findatruck/convex-client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --dts --format esm,cjs --out-dir dist --clean",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@findatruck/shared-schemas": "^2.9.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"zod": "^4.1.11"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^24.7.0",
|
|
33
|
+
"tsup": "^8.0.0",
|
|
34
|
+
"typescript": "^5.9.3",
|
|
35
|
+
"vitest": "^3.2.4",
|
|
36
|
+
"zod": "^4.1.11"
|
|
37
|
+
}
|
|
38
|
+
}
|