@striae-org/striae 4.3.4 → 5.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.
Files changed (61) hide show
  1. package/.env.example +9 -2
  2. package/app/components/actions/case-export/download-handlers.ts +66 -11
  3. package/app/components/actions/case-import/confirmation-import.ts +50 -7
  4. package/app/components/actions/case-import/confirmation-package.ts +99 -22
  5. package/app/components/actions/case-import/orchestrator.ts +116 -13
  6. package/app/components/actions/case-import/validation.ts +171 -7
  7. package/app/components/actions/case-import/zip-processing.ts +224 -127
  8. package/app/components/actions/case-manage.ts +74 -15
  9. package/app/components/actions/confirm-export.ts +32 -3
  10. package/app/components/actions/generate-pdf.ts +43 -1
  11. package/app/components/actions/image-manage.ts +13 -45
  12. package/app/components/navbar/navbar.module.css +0 -10
  13. package/app/components/navbar/navbar.tsx +0 -22
  14. package/app/components/sidebar/case-import/case-import.module.css +7 -131
  15. package/app/components/sidebar/case-import/case-import.tsx +7 -14
  16. package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +17 -60
  17. package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +23 -39
  18. package/app/components/sidebar/case-import/components/ConfirmationPreviewSection.tsx +5 -45
  19. package/app/components/sidebar/case-import/components/FileSelector.tsx +5 -6
  20. package/app/components/sidebar/case-import/hooks/useFilePreview.ts +2 -48
  21. package/app/components/sidebar/case-import/utils/file-validation.ts +9 -21
  22. package/app/config-example/config.json +5 -0
  23. package/app/routes/auth/login.tsx +1 -1
  24. package/app/routes/striae/hooks/use-striae-reset-helpers.ts +4 -0
  25. package/app/routes/striae/striae.tsx +15 -4
  26. package/app/utils/data/operations/case-operations.ts +13 -1
  27. package/app/utils/data/operations/confirmation-summary-operations.ts +38 -1
  28. package/app/utils/data/operations/file-annotation-operations.ts +13 -1
  29. package/app/utils/data/operations/signing-operations.ts +93 -0
  30. package/app/utils/data/operations/types.ts +6 -0
  31. package/app/utils/forensics/export-encryption.ts +316 -0
  32. package/app/utils/forensics/export-verification.ts +1 -409
  33. package/app/utils/forensics/index.ts +1 -0
  34. package/app/utils/ui/case-messages.ts +5 -2
  35. package/package.json +2 -2
  36. package/scripts/deploy-config.sh +244 -7
  37. package/scripts/deploy-pages-secrets.sh +0 -6
  38. package/scripts/deploy-worker-secrets.sh +66 -5
  39. package/scripts/encrypt-r2-backfill.mjs +376 -0
  40. package/worker-configuration.d.ts +13 -7
  41. package/workers/audit-worker/package.json +1 -4
  42. package/workers/audit-worker/src/audit-worker.example.ts +522 -61
  43. package/workers/audit-worker/wrangler.jsonc.example +6 -1
  44. package/workers/data-worker/package.json +1 -4
  45. package/workers/data-worker/src/data-worker.example.ts +409 -1
  46. package/workers/data-worker/src/encryption-utils.ts +269 -0
  47. package/workers/data-worker/worker-configuration.d.ts +1 -1
  48. package/workers/data-worker/wrangler.jsonc.example +6 -2
  49. package/workers/image-worker/package.json +1 -4
  50. package/workers/image-worker/src/encryption-utils.ts +217 -0
  51. package/workers/image-worker/src/image-worker.example.ts +196 -127
  52. package/workers/image-worker/wrangler.jsonc.example +8 -1
  53. package/workers/keys-worker/package.json +1 -4
  54. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  55. package/workers/pdf-worker/package.json +1 -4
  56. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  57. package/workers/user-worker/package.json +1 -4
  58. package/workers/user-worker/wrangler.jsonc.example +1 -1
  59. package/wrangler.toml.example +1 -1
  60. package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +0 -287
  61. package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +0 -470
@@ -0,0 +1,376 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+
7
+ const DEFAULT_STATE_FILE = '.backfill-data-at-rest-state.json';
8
+ const DEFAULT_BATCH_SIZE = 100;
9
+ const MAX_BATCH_SIZE = 1000;
10
+
11
+ function parseArgs(argv) {
12
+ const args = {
13
+ dryRun: false,
14
+ prefix: '',
15
+ cursor: undefined,
16
+ resume: false,
17
+ maxBatches: undefined,
18
+ batchSize: DEFAULT_BATCH_SIZE,
19
+ workerDomain: undefined,
20
+ r2Secret: undefined,
21
+ stateFile: DEFAULT_STATE_FILE,
22
+ keepState: false,
23
+ timeoutMs: 60000
24
+ };
25
+
26
+ for (let index = 0; index < argv.length; index += 1) {
27
+ const token = argv[index];
28
+
29
+ if (token === '--help' || token === '-h') {
30
+ printHelp();
31
+ process.exit(0);
32
+ }
33
+
34
+ if (token === '--dry-run') {
35
+ args.dryRun = true;
36
+ continue;
37
+ }
38
+
39
+ if (token === '--resume') {
40
+ args.resume = true;
41
+ continue;
42
+ }
43
+
44
+ if (token === '--keep-state') {
45
+ args.keepState = true;
46
+ continue;
47
+ }
48
+
49
+ const valueToken = argv[index + 1];
50
+
51
+ if (token === '--prefix') {
52
+ args.prefix = valueToken ?? '';
53
+ index += 1;
54
+ continue;
55
+ }
56
+
57
+ if (token === '--cursor') {
58
+ args.cursor = valueToken;
59
+ index += 1;
60
+ continue;
61
+ }
62
+
63
+ if (token === '--state-file') {
64
+ args.stateFile = valueToken ?? DEFAULT_STATE_FILE;
65
+ index += 1;
66
+ continue;
67
+ }
68
+
69
+ if (token === '--worker-domain') {
70
+ args.workerDomain = valueToken;
71
+ index += 1;
72
+ continue;
73
+ }
74
+
75
+ if (token === '--r2-secret') {
76
+ args.r2Secret = valueToken;
77
+ index += 1;
78
+ continue;
79
+ }
80
+
81
+ if (token === '--max-batches') {
82
+ const parsed = Number(valueToken);
83
+ if (!Number.isFinite(parsed) || parsed < 1) {
84
+ throw new Error('Invalid --max-batches value. It must be a positive number.');
85
+ }
86
+ args.maxBatches = Math.floor(parsed);
87
+ index += 1;
88
+ continue;
89
+ }
90
+
91
+ if (token === '--batch-size') {
92
+ const parsed = Number(valueToken);
93
+ if (!Number.isFinite(parsed) || parsed < 1 || parsed > MAX_BATCH_SIZE) {
94
+ throw new Error(`Invalid --batch-size value. Use a number between 1 and ${MAX_BATCH_SIZE}.`);
95
+ }
96
+ args.batchSize = Math.floor(parsed);
97
+ index += 1;
98
+ continue;
99
+ }
100
+
101
+ if (token === '--timeout-ms') {
102
+ const parsed = Number(valueToken);
103
+ if (!Number.isFinite(parsed) || parsed < 1000) {
104
+ throw new Error('Invalid --timeout-ms value. It must be >= 1000.');
105
+ }
106
+ args.timeoutMs = Math.floor(parsed);
107
+ index += 1;
108
+ continue;
109
+ }
110
+
111
+ throw new Error(`Unknown argument: ${token}`);
112
+ }
113
+
114
+ return args;
115
+ }
116
+
117
+ function printHelp() {
118
+ console.log('Usage: node scripts/encrypt-r2-backfill.mjs [options]');
119
+ console.log('');
120
+ console.log('Options:');
121
+ console.log(' --dry-run Scan and report, do not write encrypted objects');
122
+ console.log(' --prefix <value> Restrict scan to an R2 key prefix');
123
+ console.log(' --batch-size <1-1000> Number of objects per request (default: 100)');
124
+ console.log(' --max-batches <n> Stop after n batches');
125
+ console.log(' --cursor <token> Start from an explicit cursor');
126
+ console.log(' --resume Resume from state file cursor');
127
+ console.log(' --state-file <path> State file path (default: .backfill-data-at-rest-state.json)');
128
+ console.log(' --keep-state Keep state file even when completed');
129
+ console.log(' --worker-domain <host> Override DATA_WORKER_DOMAIN from .env');
130
+ console.log(' --r2-secret <value> Override R2_KEY_SECRET from .env');
131
+ console.log(' --timeout-ms <ms> Request timeout (default: 60000)');
132
+ console.log(' -h, --help Show this help text');
133
+ }
134
+
135
+ function parseDotEnv(filePath) {
136
+ if (!fs.existsSync(filePath)) {
137
+ return {};
138
+ }
139
+
140
+ const content = fs.readFileSync(filePath, 'utf8');
141
+ const env = {};
142
+
143
+ for (const line of content.split(/\r?\n/)) {
144
+ const trimmed = line.trim();
145
+ if (!trimmed || trimmed.startsWith('#')) {
146
+ continue;
147
+ }
148
+
149
+ const separatorIndex = trimmed.indexOf('=');
150
+ if (separatorIndex <= 0) {
151
+ continue;
152
+ }
153
+
154
+ const key = trimmed.slice(0, separatorIndex).trim();
155
+ let value = trimmed.slice(separatorIndex + 1).trim();
156
+
157
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
158
+ value = value.slice(1, -1);
159
+ }
160
+
161
+ env[key] = value;
162
+ }
163
+
164
+ return env;
165
+ }
166
+
167
+ function getConfigValue(name, overrides, envMap) {
168
+ const overrideValue = overrides[name];
169
+ if (typeof overrideValue === 'string' && overrideValue.length > 0) {
170
+ return overrideValue;
171
+ }
172
+
173
+ const processValue = process.env[name];
174
+ if (typeof processValue === 'string' && processValue.length > 0) {
175
+ return processValue;
176
+ }
177
+
178
+ const envFileValue = envMap[name];
179
+ if (typeof envFileValue === 'string' && envFileValue.length > 0) {
180
+ return envFileValue;
181
+ }
182
+
183
+ return '';
184
+ }
185
+
186
+ function readStateFile(filePath) {
187
+ if (!fs.existsSync(filePath)) {
188
+ return null;
189
+ }
190
+
191
+ try {
192
+ const raw = fs.readFileSync(filePath, 'utf8');
193
+ return JSON.parse(raw);
194
+ } catch {
195
+ return null;
196
+ }
197
+ }
198
+
199
+ function writeStateFile(filePath, state) {
200
+ fs.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
201
+ }
202
+
203
+ function removeStateFile(filePath) {
204
+ if (fs.existsSync(filePath)) {
205
+ fs.unlinkSync(filePath);
206
+ }
207
+ }
208
+
209
+ async function postBackfillBatch({ workerDomain, r2Secret, body, timeoutMs }) {
210
+ const controller = new AbortController();
211
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
212
+
213
+ try {
214
+ const response = await fetch(`https://${workerDomain}/api/admin/data-at-rest-backfill`, {
215
+ method: 'POST',
216
+ headers: {
217
+ 'Content-Type': 'application/json',
218
+ 'X-Custom-Auth-Key': r2Secret
219
+ },
220
+ body: JSON.stringify(body),
221
+ signal: controller.signal
222
+ });
223
+
224
+ const json = await response.json().catch(() => null);
225
+
226
+ if (!response.ok) {
227
+ const detail = json && typeof json.error === 'string' ? json.error : `${response.status} ${response.statusText}`;
228
+ throw new Error(`Backfill request failed: ${detail}`);
229
+ }
230
+
231
+ if (!json || typeof json !== 'object') {
232
+ throw new Error('Backfill response was not valid JSON');
233
+ }
234
+
235
+ return json;
236
+ } finally {
237
+ clearTimeout(timeoutId);
238
+ }
239
+ }
240
+
241
+ function formatNumber(value) {
242
+ return new Intl.NumberFormat('en-US').format(value);
243
+ }
244
+
245
+ async function main() {
246
+ const args = parseArgs(process.argv.slice(2));
247
+ const envPath = path.resolve(process.cwd(), '.env');
248
+ const envFromFile = parseDotEnv(envPath);
249
+
250
+ const workerDomain = getConfigValue('DATA_WORKER_DOMAIN', { DATA_WORKER_DOMAIN: args.workerDomain }, envFromFile)
251
+ .replace(/^https?:\/\//, '')
252
+ .replace(/\/+$/, '');
253
+ const r2Secret = getConfigValue('R2_KEY_SECRET', { R2_KEY_SECRET: args.r2Secret }, envFromFile);
254
+
255
+ if (!workerDomain) {
256
+ throw new Error('Missing DATA_WORKER_DOMAIN. Set it in .env or pass --worker-domain.');
257
+ }
258
+
259
+ if (!r2Secret) {
260
+ throw new Error('Missing R2_KEY_SECRET. Set it in .env or pass --r2-secret.');
261
+ }
262
+
263
+ const statePath = path.resolve(process.cwd(), args.stateFile);
264
+ const priorState = args.resume ? readStateFile(statePath) : null;
265
+
266
+ let cursor = args.cursor;
267
+ if (!cursor && priorState && typeof priorState.cursor === 'string') {
268
+ cursor = priorState.cursor;
269
+ }
270
+
271
+ const totals = {
272
+ scanned: 0,
273
+ eligible: 0,
274
+ encrypted: 0,
275
+ skippedEncrypted: 0,
276
+ skippedNonJson: 0,
277
+ failed: 0,
278
+ batches: 0
279
+ };
280
+
281
+ console.log('R2 data-at-rest backfill starting');
282
+ console.log(`Worker: https://${workerDomain}`);
283
+ console.log(`Mode: ${args.dryRun ? 'dry-run' : 'write'}`);
284
+ console.log(`Prefix: ${args.prefix || '(all keys)'}`);
285
+ console.log(`Batch size: ${args.batchSize}`);
286
+ if (cursor) {
287
+ console.log(`Starting cursor: ${cursor}`);
288
+ }
289
+
290
+ while (true) {
291
+ if (args.maxBatches !== undefined && totals.batches >= args.maxBatches) {
292
+ console.log(`Stopping after max batches: ${args.maxBatches}`);
293
+ break;
294
+ }
295
+
296
+ const batchBody = {
297
+ dryRun: args.dryRun,
298
+ prefix: args.prefix,
299
+ cursor,
300
+ batchSize: args.batchSize
301
+ };
302
+
303
+ const result = await postBackfillBatch({
304
+ workerDomain,
305
+ r2Secret,
306
+ body: batchBody,
307
+ timeoutMs: args.timeoutMs
308
+ });
309
+
310
+ totals.batches += 1;
311
+ totals.scanned += Number(result.scanned || 0);
312
+ totals.eligible += Number(result.eligible || 0);
313
+ totals.encrypted += Number(result.encrypted || 0);
314
+ totals.skippedEncrypted += Number(result.skippedEncrypted || 0);
315
+ totals.skippedNonJson += Number(result.skippedNonJson || 0);
316
+ totals.failed += Number(result.failed || 0);
317
+
318
+ cursor = typeof result.nextCursor === 'string' && result.nextCursor.length > 0
319
+ ? result.nextCursor
320
+ : undefined;
321
+
322
+ writeStateFile(statePath, {
323
+ updatedAt: new Date().toISOString(),
324
+ dryRun: args.dryRun,
325
+ prefix: args.prefix,
326
+ batchSize: args.batchSize,
327
+ cursor: cursor ?? null,
328
+ hasMore: Boolean(result.hasMore),
329
+ totals
330
+ });
331
+
332
+ console.log(
333
+ `Batch ${totals.batches}: scanned=${formatNumber(Number(result.scanned || 0))}, ` +
334
+ `eligible=${formatNumber(Number(result.eligible || 0))}, ` +
335
+ `encrypted=${formatNumber(Number(result.encrypted || 0))}, ` +
336
+ `failed=${formatNumber(Number(result.failed || 0))}`
337
+ );
338
+
339
+ if (Array.isArray(result.failures) && result.failures.length > 0) {
340
+ for (const failure of result.failures) {
341
+ if (!failure || typeof failure.key !== 'string') {
342
+ continue;
343
+ }
344
+ const errorMessage = typeof failure.error === 'string' ? failure.error : 'Unknown error';
345
+ console.error(` failure: ${failure.key} -> ${errorMessage}`);
346
+ }
347
+ }
348
+
349
+ if (!result.hasMore) {
350
+ break;
351
+ }
352
+ }
353
+
354
+ if (!args.keepState && !cursor) {
355
+ removeStateFile(statePath);
356
+ }
357
+
358
+ console.log('Backfill summary');
359
+ console.log(` batches: ${formatNumber(totals.batches)}`);
360
+ console.log(` scanned: ${formatNumber(totals.scanned)}`);
361
+ console.log(` eligible: ${formatNumber(totals.eligible)}`);
362
+ console.log(` encrypted: ${formatNumber(totals.encrypted)}`);
363
+ console.log(` skippedEncrypted: ${formatNumber(totals.skippedEncrypted)}`);
364
+ console.log(` skippedNonJson: ${formatNumber(totals.skippedNonJson)}`);
365
+ console.log(` failed: ${formatNumber(totals.failed)}`);
366
+
367
+ if (totals.failed > 0) {
368
+ process.exitCode = 1;
369
+ }
370
+ }
371
+
372
+ main().catch((error) => {
373
+ const message = error instanceof Error ? error.message : 'Unknown backfill error';
374
+ console.error(`Backfill aborted: ${message}`);
375
+ process.exit(1);
376
+ });
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable */
2
- // Generated by Wrangler by running `wrangler types` (hash: 3509cf2c6fcfb4a2a6e6bd16efbe22b3)
3
- // Runtime types generated with workerd@1.20250823.0 2026-03-20 nodejs_compat
2
+ // Generated by Wrangler by running `wrangler types` (hash: f4bdacbf5374924fcd3b9a294158d34f)
3
+ // Runtime types generated with workerd@1.20250823.0 2026-03-24 nodejs_compat
4
4
  declare namespace Cloudflare {
5
5
  interface Env {
6
6
  ACCOUNT_ID: string;
@@ -27,17 +27,23 @@ declare namespace Cloudflare {
27
27
  KV_STORE_ID: string;
28
28
  DATA_WORKER_NAME: string;
29
29
  DATA_BUCKET_NAME: string;
30
+ FILES_BUCKET_NAME: string;
30
31
  DATA_WORKER_DOMAIN: string;
31
32
  MANIFEST_SIGNING_PRIVATE_KEY: string;
32
33
  MANIFEST_SIGNING_KEY_ID: string;
33
34
  MANIFEST_SIGNING_PUBLIC_KEY: string;
35
+ EXPORT_ENCRYPTION_PRIVATE_KEY: string;
36
+ EXPORT_ENCRYPTION_KEY_ID: string;
37
+ EXPORT_ENCRYPTION_PUBLIC_KEY: string;
38
+ DATA_AT_REST_ENCRYPTION_ENABLED: string;
39
+ DATA_AT_REST_ENCRYPTION_PRIVATE_KEY: string;
40
+ DATA_AT_REST_ENCRYPTION_KEY_ID: string;
41
+ DATA_AT_REST_ENCRYPTION_PUBLIC_KEY: string;
34
42
  AUDIT_WORKER_NAME: string;
35
43
  AUDIT_BUCKET_NAME: string;
36
44
  AUDIT_WORKER_DOMAIN: string;
37
45
  IMAGES_WORKER_NAME: string;
38
46
  IMAGES_WORKER_DOMAIN: string;
39
- API_TOKEN: string;
40
- HMAC_KEY: string;
41
47
  PDF_WORKER_NAME: string;
42
48
  PDF_WORKER_DOMAIN: string;
43
49
  PDF_WORKER_AUTH: string;
@@ -50,7 +56,7 @@ type StringifyValues<EnvType extends Record<string, unknown>> = {
50
56
  [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string;
51
57
  };
52
58
  declare namespace NodeJS {
53
- interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "ACCOUNT_ID" | "USER_DB_AUTH" | "R2_KEY_SECRET" | "IMAGES_API_TOKEN" | "API_KEY" | "AUTH_DOMAIN" | "PROJECT_ID" | "STORAGE_BUCKET" | "MESSAGING_SENDER_ID" | "APP_ID" | "MEASUREMENT_ID" | "FIREBASE_SERVICE_ACCOUNT_EMAIL" | "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" | "PAGES_PROJECT_NAME" | "PAGES_CUSTOM_DOMAIN" | "KEYS_WORKER_NAME" | "KEYS_WORKER_DOMAIN" | "KEYS_AUTH" | "ACCOUNT_HASH" | "USER_WORKER_NAME" | "USER_WORKER_DOMAIN" | "KV_STORE_ID" | "DATA_WORKER_NAME" | "DATA_BUCKET_NAME" | "DATA_WORKER_DOMAIN" | "MANIFEST_SIGNING_PRIVATE_KEY" | "MANIFEST_SIGNING_KEY_ID" | "MANIFEST_SIGNING_PUBLIC_KEY" | "AUDIT_WORKER_NAME" | "AUDIT_BUCKET_NAME" | "AUDIT_WORKER_DOMAIN" | "IMAGES_WORKER_NAME" | "IMAGES_WORKER_DOMAIN" | "API_TOKEN" | "HMAC_KEY" | "PDF_WORKER_NAME" | "PDF_WORKER_DOMAIN" | "PDF_WORKER_AUTH" | "BROWSER_API_TOKEN" | "PRIMERSHEAR_EMAILS">> {}
59
+ interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "ACCOUNT_ID" | "USER_DB_AUTH" | "R2_KEY_SECRET" | "IMAGES_API_TOKEN" | "API_KEY" | "AUTH_DOMAIN" | "PROJECT_ID" | "STORAGE_BUCKET" | "MESSAGING_SENDER_ID" | "APP_ID" | "MEASUREMENT_ID" | "FIREBASE_SERVICE_ACCOUNT_EMAIL" | "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" | "PAGES_PROJECT_NAME" | "PAGES_CUSTOM_DOMAIN" | "KEYS_WORKER_NAME" | "KEYS_WORKER_DOMAIN" | "KEYS_AUTH" | "ACCOUNT_HASH" | "USER_WORKER_NAME" | "USER_WORKER_DOMAIN" | "KV_STORE_ID" | "DATA_WORKER_NAME" | "DATA_BUCKET_NAME" | "FILES_BUCKET_NAME" | "DATA_WORKER_DOMAIN" | "MANIFEST_SIGNING_PRIVATE_KEY" | "MANIFEST_SIGNING_KEY_ID" | "MANIFEST_SIGNING_PUBLIC_KEY" | "EXPORT_ENCRYPTION_PRIVATE_KEY" | "EXPORT_ENCRYPTION_KEY_ID" | "EXPORT_ENCRYPTION_PUBLIC_KEY" | "DATA_AT_REST_ENCRYPTION_ENABLED" | "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" | "DATA_AT_REST_ENCRYPTION_KEY_ID" | "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" | "AUDIT_WORKER_NAME" | "AUDIT_BUCKET_NAME" | "AUDIT_WORKER_DOMAIN" | "IMAGES_WORKER_NAME" | "IMAGES_WORKER_DOMAIN" | "PDF_WORKER_NAME" | "PDF_WORKER_DOMAIN" | "PDF_WORKER_AUTH" | "BROWSER_API_TOKEN" | "PRIMERSHEAR_EMAILS">> {}
54
60
  }
55
61
 
56
62
  // Begin runtime types
@@ -5485,7 +5491,7 @@ type AIGatewayHeaders = {
5485
5491
  [key: string]: string | number | boolean | object;
5486
5492
  };
5487
5493
  type AIGatewayUniversalRequest = {
5488
- provider: AIGatewayProviders | string;
5494
+ provider: AIGatewayProviders | string; // eslint-disable-line
5489
5495
  endpoint: string;
5490
5496
  headers: Partial<AIGatewayHeaders>;
5491
5497
  query: unknown;
@@ -5501,7 +5507,7 @@ declare abstract class AiGateway {
5501
5507
  gateway?: UniversalGatewayOptions;
5502
5508
  extraHeaders?: object;
5503
5509
  }): Promise<Response>;
5504
- getUrl(provider?: AIGatewayProviders | string): Promise<string>;
5510
+ getUrl(provider?: AIGatewayProviders | string): Promise<string>; // eslint-disable-line
5505
5511
  }
5506
5512
  interface AutoRAGInternalError extends Error {
5507
5513
  }
@@ -5,13 +5,10 @@
5
5
  "scripts": {
6
6
  "deploy": "wrangler deploy",
7
7
  "dev": "wrangler dev",
8
- "start": "wrangler dev",
9
- "test": "vitest"
8
+ "start": "wrangler dev"
10
9
  },
11
10
  "devDependencies": {
12
11
  "@cloudflare/puppeteer": "^1.0.6",
13
- "@cloudflare/vitest-pool-workers": "^0.13.0",
14
- "vitest": "~4.1.0",
15
12
  "wrangler": "^4.76.0"
16
13
  },
17
14
  "overrides": {