triangle-utils 1.3.3 → 1.4.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/src/UtilsBedrock.d.ts +8 -0
- package/dist/src/{Utils_Bedrock.js → UtilsBedrock.js} +7 -4
- package/dist/src/UtilsBee.d.ts +8 -0
- package/dist/src/{Utils_Bee.js → UtilsBee.js} +13 -8
- package/dist/src/UtilsCognito.d.ts +12 -0
- package/dist/src/UtilsCognito.js +110 -0
- package/dist/src/{Utils_DynamoDB.d.ts → UtilsDynamoDB.d.ts} +8 -10
- package/dist/src/{Utils_DynamoDB.js → UtilsDynamoDB.js} +22 -22
- package/dist/src/UtilsMisc.d.ts +32 -0
- package/dist/src/UtilsMisc.js +156 -0
- package/dist/src/{Utils_S3.d.ts → UtilsS3.d.ts} +5 -7
- package/dist/src/{Utils_S3.js → UtilsS3.js} +3 -3
- package/dist/src/{Utils_S3Vectors.d.ts → UtilsS3Vectors.d.ts} +6 -9
- package/dist/src/{Utils_S3Vectors.js → UtilsS3Vectors.js} +4 -4
- package/dist/src/UtilsYoutube.d.ts +6 -0
- package/dist/src/{Utils_Youtube.js → UtilsYoutube.js} +8 -7
- package/dist/src/index.d.ts +28 -0
- package/dist/src/index.js +36 -0
- package/dist/src/types/{Config.d.ts → TriangleUtilsConfig.d.ts} +1 -3
- package/package.json +8 -8
- package/src/{Utils_Bedrock.ts → UtilsBedrock.ts} +10 -8
- package/src/{Utils_Bee.ts → UtilsBee.ts} +21 -15
- package/src/UtilsCognito.ts +116 -0
- package/src/{Utils_DynamoDB.ts → UtilsDynamoDB.ts} +32 -30
- package/src/UtilsMisc.ts +188 -0
- package/src/{Utils_S3.ts → UtilsS3.ts} +4 -5
- package/src/{Utils_S3Vectors.ts → UtilsS3Vectors.ts} +6 -7
- package/src/{Utils_Youtube.ts → UtilsYoutube.ts} +9 -9
- package/src/index.ts +43 -0
- package/src/types/TriangleUtilsConfig.ts +6 -0
- package/test/Utils.test.ts +135 -24
- package/tsconfig.json +1 -1
- package/vite.config.ts +10 -0
- package/dist/src/Utils.d.ts +0 -17
- package/dist/src/Utils.js +0 -24
- package/dist/src/Utils_Bedrock.d.ts +0 -10
- package/dist/src/Utils_Bee.d.ts +0 -11
- package/dist/src/Utils_Misc.d.ts +0 -30
- package/dist/src/Utils_Misc.js +0 -627
- package/dist/src/Utils_Youtube.d.ts +0 -8
- package/src/Utils.ts +0 -28
- package/src/Utils_Misc.ts +0 -655
- package/src/types/Config.ts +0 -9
- /package/dist/src/types/{Config.js → TriangleUtilsConfig.js} +0 -0
package/src/UtilsMisc.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import * as nodemailer from "nodemailer"
|
|
2
|
+
import * as crypto from "crypto"
|
|
3
|
+
|
|
4
|
+
import { TriangleUtilsConfig } from "./types/TriangleUtilsConfig"
|
|
5
|
+
|
|
6
|
+
interface Encryption {
|
|
7
|
+
iv : string,
|
|
8
|
+
ciphertext : string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class UtilsMisc {
|
|
12
|
+
|
|
13
|
+
readonly config : TriangleUtilsConfig
|
|
14
|
+
readonly transporter
|
|
15
|
+
|
|
16
|
+
constructor(config : TriangleUtilsConfig) {
|
|
17
|
+
this.config = config
|
|
18
|
+
if (config.google_email !== undefined && config.google_app_password !== undefined) {
|
|
19
|
+
this.transporter = nodemailer.createTransport({
|
|
20
|
+
service: "Gmail",
|
|
21
|
+
host: "smtp.gmail.com",
|
|
22
|
+
port: 465,
|
|
23
|
+
secure: true,
|
|
24
|
+
auth: {
|
|
25
|
+
user: config.google_email,
|
|
26
|
+
pass: config.google_app_password,
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async send_email(recipient : string, subject : string, text : string) : Promise<boolean> {
|
|
33
|
+
if (this.transporter === undefined) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
for (let i = 0; i < 3; i++) {
|
|
37
|
+
try {
|
|
38
|
+
await this.transporter.sendMail({
|
|
39
|
+
from: this.config.alerts_email,
|
|
40
|
+
to: recipient,
|
|
41
|
+
subject: subject,
|
|
42
|
+
text: text
|
|
43
|
+
})
|
|
44
|
+
return true
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.log("EMAIL ERROR", error)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async admin_alert(text : string) : Promise<boolean> {
|
|
53
|
+
console.log("ADMIN ALERT:", text)
|
|
54
|
+
return await this.send_email(this.config.google_email, "ADMIN ALERT", text)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async safe_run(f : () => Promise<any>) : Promise<void> {
|
|
58
|
+
try {
|
|
59
|
+
await f()
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (!(error instanceof Error)) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
if (error.stack !== undefined) {
|
|
65
|
+
await this.admin_alert(error.stack.toString())
|
|
66
|
+
} else {
|
|
67
|
+
await this.admin_alert(error.toString())
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async iterate<T>(inputs : T[], f : (input : T) => Promise<any>, concurrency : number = 1, print_indices : boolean = false) {
|
|
73
|
+
let index = 0
|
|
74
|
+
let iterators = []
|
|
75
|
+
for (let i = 0; i < concurrency; i++) {
|
|
76
|
+
iterators.push((async () => {
|
|
77
|
+
while (index < inputs.length) {
|
|
78
|
+
index += 1
|
|
79
|
+
if (print_indices) {
|
|
80
|
+
console.log(i + ":" + (index - 1) + "/" + inputs.length)
|
|
81
|
+
}
|
|
82
|
+
await this.safe_run(() => f(inputs[index - 1]))
|
|
83
|
+
}
|
|
84
|
+
})())
|
|
85
|
+
}
|
|
86
|
+
await Promise.all(iterators)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
static async wait(duration : number) {
|
|
91
|
+
return new Promise(resolve => setTimeout(resolve, duration))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static get_current_time() {
|
|
95
|
+
return (new Date()).toISOString()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static get_current_milliseconds() {
|
|
99
|
+
return (new Date()).getTime()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static async sha256(input : string) : Promise<string> {
|
|
103
|
+
const text_encoder = new TextEncoder()
|
|
104
|
+
const input_buffer = text_encoder.encode(input);
|
|
105
|
+
|
|
106
|
+
const hash_buffer = await crypto.subtle.digest("SHA-256", input_buffer)
|
|
107
|
+
|
|
108
|
+
const hash_array = Array.from(new Uint8Array(hash_buffer))
|
|
109
|
+
|
|
110
|
+
const hash = hash_array.map(b => b.toString(16).padStart(2, "0")).join("")
|
|
111
|
+
return hash
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static encrypt(secret_key : string, text : string) : Encryption {
|
|
115
|
+
const iv = crypto.randomBytes(16)
|
|
116
|
+
const cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(secret_key, "hex"), iv)
|
|
117
|
+
let ciphertext = cipher.update(text, "utf8", "hex")
|
|
118
|
+
ciphertext += cipher.final("hex")
|
|
119
|
+
return {
|
|
120
|
+
iv: iv.toString("hex"),
|
|
121
|
+
ciphertext: ciphertext
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static decrypt(secret_key : string, encryption : Encryption) : string {
|
|
126
|
+
const iv = encryption.iv
|
|
127
|
+
const ciphertext = encryption.ciphertext
|
|
128
|
+
const decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(secret_key, "hex"), Buffer.from(iv, "hex"))
|
|
129
|
+
let text = decipher.update(ciphertext, "hex", "utf8")
|
|
130
|
+
text += decipher.final("utf8")
|
|
131
|
+
return text
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static get_secret_hash(username : string, cognito_client_id : string, cognito_client_secret : string) : string {
|
|
135
|
+
const secret_hash = crypto.createHmac("sha256", cognito_client_secret)
|
|
136
|
+
.update(username + cognito_client_id)
|
|
137
|
+
.digest("base64")
|
|
138
|
+
return secret_hash
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static get_chunk_indices(text_length : number, max_length = 2500, overlap = 50) : [number, number][] {
|
|
142
|
+
const num_chunks = Math.ceil((text_length + overlap) / max_length)
|
|
143
|
+
const chunk_length = Math.ceil((text_length - overlap) / num_chunks + overlap)
|
|
144
|
+
const chunk_indices : [number, number][] = []
|
|
145
|
+
for (let i = 0; i < num_chunks; i++) {
|
|
146
|
+
chunk_indices.push([
|
|
147
|
+
(chunk_length - overlap) * i,
|
|
148
|
+
(chunk_length - overlap) * i + chunk_length
|
|
149
|
+
])
|
|
150
|
+
}
|
|
151
|
+
return chunk_indices
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static chunkify(text : string, max_length = 2500, overlap = 50) : {
|
|
155
|
+
chunk_index : [number, number],
|
|
156
|
+
chunk_text : string
|
|
157
|
+
}[] {
|
|
158
|
+
const chunk_indices = this.get_chunk_indices(text.length, max_length, overlap)
|
|
159
|
+
const chunks = chunk_indices.map(chunk_index => ({
|
|
160
|
+
chunk_index : chunk_index,
|
|
161
|
+
chunk_text : text.substring(...chunk_index)
|
|
162
|
+
}))
|
|
163
|
+
return chunks
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static chunkify_lined(text : string, max_length = 2500, overlap = 50) : {
|
|
167
|
+
chunk_index : [number, number],
|
|
168
|
+
chunk_text : string
|
|
169
|
+
}[] {
|
|
170
|
+
const chunk_indices : [number, number][] = this.get_chunk_indices(text.length, max_length, overlap)
|
|
171
|
+
const lined_chunk_indices : [number, number][] = []
|
|
172
|
+
for (const chunk_index of chunk_indices) {
|
|
173
|
+
const lined_chunk_index : [number, number] = [
|
|
174
|
+
text.lastIndexOf("\n", chunk_index[0]) + 1,
|
|
175
|
+
text.indexOf("\n", chunk_index[1]) + 1
|
|
176
|
+
]
|
|
177
|
+
if (lined_chunk_index[1] < lined_chunk_index[0]) {
|
|
178
|
+
lined_chunk_index[1] += text.length
|
|
179
|
+
}
|
|
180
|
+
lined_chunk_indices.push(lined_chunk_index)
|
|
181
|
+
}
|
|
182
|
+
return lined_chunk_indices.map(chunk_index => ({
|
|
183
|
+
chunk_index : chunk_index,
|
|
184
|
+
chunk_text : text.substring(...chunk_index)
|
|
185
|
+
}))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { S3, NoSuchKey, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3"
|
|
2
2
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"
|
|
3
|
-
import { Config } from "./types/Config"
|
|
4
3
|
|
|
5
4
|
interface S3Key {
|
|
6
5
|
Bucket : string,
|
|
@@ -17,12 +16,12 @@ function parse_s3_filename(s3_filename : string) : S3Key | undefined {
|
|
|
17
16
|
return { Bucket : bucket, Key : filename }
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
export class
|
|
19
|
+
export class UtilsS3 {
|
|
21
20
|
|
|
22
|
-
readonly s3 : S3
|
|
21
|
+
private readonly s3 : S3
|
|
23
22
|
|
|
24
|
-
constructor(
|
|
25
|
-
this.s3 = new S3({ region :
|
|
23
|
+
constructor(region : string) {
|
|
24
|
+
this.s3 = new S3({ region : region })
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
async file_exists(s3_filename : string) {
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { ListVectorsCommandInput, S3Vectors } from "@aws-sdk/client-s3vectors"
|
|
2
|
-
import { Config } from "./types/Config"
|
|
3
2
|
|
|
4
|
-
interface Vector {
|
|
3
|
+
export interface Vector {
|
|
5
4
|
key : string,
|
|
6
5
|
data : number[],
|
|
7
6
|
metadata : any
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
export class
|
|
9
|
+
export class UtilsS3Vectors {
|
|
11
10
|
|
|
12
|
-
readonly s3vectors : S3Vectors
|
|
11
|
+
private readonly s3vectors : S3Vectors
|
|
13
12
|
|
|
14
|
-
constructor(
|
|
15
|
-
this.s3vectors = new S3Vectors({ region :
|
|
13
|
+
constructor(region : string) {
|
|
14
|
+
this.s3vectors = new S3Vectors({ region : region })
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
async scan(vector_bucket : string, vector_index : string) : Promise<string[]> {
|
|
@@ -134,7 +133,7 @@ export class Utils_S3Vectors {
|
|
|
134
133
|
}
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
async query(vector_bucket : string, vector_index : string, data : number[], num_vectors : number, filters : Record<string, any> =
|
|
136
|
+
async query(vector_bucket : string, vector_index : string, data : number[], num_vectors : number, filters : Record<string, any> | undefined = undefined) {
|
|
138
137
|
const response = await this.s3vectors.queryVectors({
|
|
139
138
|
vectorBucketName : vector_bucket,
|
|
140
139
|
indexName : vector_index,
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { youtube_v3 } from "googleapis"
|
|
2
|
-
import { Config } from "./types/Config"
|
|
3
2
|
|
|
4
|
-
export class
|
|
3
|
+
export class UtilsYoutube {
|
|
5
4
|
|
|
6
|
-
readonly
|
|
7
|
-
readonly youtube : youtube_v3.Youtube
|
|
5
|
+
readonly youtube : youtube_v3.Youtube | undefined
|
|
8
6
|
|
|
9
|
-
constructor(
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
})
|
|
7
|
+
constructor(youtube_api_key : string | undefined) {
|
|
8
|
+
this.youtube = youtube_api_key !== undefined ? new youtube_v3.Youtube({
|
|
9
|
+
auth: youtube_api_key
|
|
10
|
+
}) : undefined
|
|
14
11
|
}
|
|
15
12
|
|
|
16
13
|
async batch_get_videos(video_ids : string[], attributes : string[] = ["snippet", "contentDetails"]) {
|
|
14
|
+
if (this.youtube === undefined) {
|
|
15
|
+
return undefined
|
|
16
|
+
}
|
|
17
17
|
for (let i = 0; i < 3; i++) {
|
|
18
18
|
try {
|
|
19
19
|
const response = await this.youtube.videos.list({
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { TriangleUtilsConfig } from "./types/TriangleUtilsConfig"
|
|
2
|
+
import { UtilsDynamoDB } from "./UtilsDynamoDB"
|
|
3
|
+
import { UtilsS3 } from "./UtilsS3"
|
|
4
|
+
import { UtilsBedrock } from "./UtilsBedrock"
|
|
5
|
+
import { UtilsBee } from "./UtilsBee"
|
|
6
|
+
import { UtilsS3Vectors } from "./UtilsS3Vectors"
|
|
7
|
+
import { UtilsCognito } from "./UtilsCognito"
|
|
8
|
+
import { UtilsMisc } from "./UtilsMisc"
|
|
9
|
+
import { UtilsYoutube } from "./UtilsYoutube"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export class TriangleUtils extends UtilsMisc {
|
|
14
|
+
|
|
15
|
+
readonly dynamodb : UtilsDynamoDB
|
|
16
|
+
readonly s3 : UtilsS3
|
|
17
|
+
readonly s3vectors : UtilsS3Vectors
|
|
18
|
+
readonly bedrock : UtilsBedrock
|
|
19
|
+
readonly cognito : UtilsCognito
|
|
20
|
+
readonly bee : UtilsBee
|
|
21
|
+
readonly youtube : UtilsYoutube
|
|
22
|
+
|
|
23
|
+
constructor(config : TriangleUtilsConfig) {
|
|
24
|
+
super(config)
|
|
25
|
+
this.dynamodb = new UtilsDynamoDB(config.region)
|
|
26
|
+
this.s3 = new UtilsS3(config.region)
|
|
27
|
+
this.s3vectors = new UtilsS3Vectors(config.region)
|
|
28
|
+
this.bedrock = new UtilsBedrock(config.region)
|
|
29
|
+
this.cognito = new UtilsCognito(config.region)
|
|
30
|
+
this.bee = new UtilsBee(config.scraping_bee_api_key)
|
|
31
|
+
this.youtube = new UtilsYoutube(config.youtube_api_key)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export * from "./types/TriangleUtilsConfig"
|
|
36
|
+
export * from "./UtilsMisc"
|
|
37
|
+
export * from "./UtilsDynamoDB"
|
|
38
|
+
export * from "./UtilsS3"
|
|
39
|
+
export * from "./UtilsBedrock"
|
|
40
|
+
export * from "./UtilsBee"
|
|
41
|
+
export * from "./UtilsS3Vectors"
|
|
42
|
+
export * from "./UtilsCognito"
|
|
43
|
+
export * from "./UtilsYoutube"
|
package/test/Utils.test.ts
CHANGED
|
@@ -1,50 +1,57 @@
|
|
|
1
|
-
import { Config } from "../src/types/Config.ts"
|
|
2
|
-
import { Utils } from "../src/Utils.ts"
|
|
3
1
|
import { SecretsManager } from "@aws-sdk/client-secrets-manager"
|
|
4
2
|
|
|
3
|
+
import { beforeAll, test, describe, expect } from "vitest"
|
|
4
|
+
|
|
5
|
+
import { TriangleUtils, TriangleUtilsConfig, UtilsMisc } from "../src"
|
|
6
|
+
|
|
5
7
|
const region = "us-east-1"
|
|
6
8
|
|
|
7
|
-
let config :
|
|
9
|
+
let config : TriangleUtilsConfig = { region : region }
|
|
8
10
|
|
|
9
11
|
beforeAll(async () => {
|
|
10
|
-
|
|
12
|
+
const secret_name = "api_keys"
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
const secrets_manager = new SecretsManager({ region: "us-east-1" })
|
|
15
|
+
|
|
16
|
+
const response = await secrets_manager.getSecretValue({
|
|
17
|
+
SecretId: secret_name
|
|
18
|
+
})
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
if (response === undefined || response.SecretString === undefined) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
const api_keys = JSON.parse(response.SecretString)
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
config = {
|
|
26
|
+
google_email : "louishou@triangleanalytics.com",
|
|
27
|
+
alerts_email : "alerts@triangleanalytics.com",
|
|
28
|
+
region : "us-east-1",
|
|
29
|
+
...api_keys
|
|
30
|
+
}
|
|
24
31
|
|
|
25
32
|
})
|
|
26
33
|
|
|
27
34
|
describe("Testing Utils", () => {
|
|
28
35
|
describe("Testing Misc", () => {
|
|
29
36
|
test("email", async () => {
|
|
30
|
-
const utils = new
|
|
31
|
-
await utils.admin_alert("Testing Admin Alert.")
|
|
37
|
+
const utils = new TriangleUtils(config)
|
|
38
|
+
const success = await utils.admin_alert("Testing Admin Alert.")
|
|
39
|
+
expect(success).toBeTruthy()
|
|
32
40
|
})
|
|
33
41
|
test("sha256", async () => {
|
|
34
|
-
const
|
|
35
|
-
const hash = await utils.sha256("Triangle Analytics")
|
|
42
|
+
const hash = await UtilsMisc.sha256("Triangle Analytics")
|
|
36
43
|
expect(hash).toEqual("7f6f79a3fdee7c38bb3b0381211575a35ffcfcf5867d6045b07b4181e6f24153")
|
|
37
44
|
})
|
|
38
45
|
})
|
|
39
46
|
describe("Testing DynamoDB", () => {
|
|
40
47
|
test("scan", async () => {
|
|
41
|
-
const utils = new
|
|
48
|
+
const utils = new TriangleUtils(config)
|
|
42
49
|
const candidates = await utils.dynamodb.scan("candidates")
|
|
43
50
|
expect(candidates).toBeDefined()
|
|
44
51
|
expect(candidates.length).toBeGreaterThan(3000)
|
|
45
|
-
})
|
|
52
|
+
}, 20000)
|
|
46
53
|
test("get; single key", async () => {
|
|
47
|
-
const utils = new
|
|
54
|
+
const utils = new TriangleUtils(config)
|
|
48
55
|
const candidate = await utils.dynamodb.get("candidates", { candidate_id : "S6MI00392" })
|
|
49
56
|
expect(candidate).toBeDefined()
|
|
50
57
|
if (candidate === undefined) {
|
|
@@ -53,7 +60,7 @@ describe("Testing Utils", () => {
|
|
|
53
60
|
expect(candidate.fec_name).toBeDefined()
|
|
54
61
|
})
|
|
55
62
|
test("get; composite key", async () => {
|
|
56
|
-
const utils = new
|
|
63
|
+
const utils = new TriangleUtils(config)
|
|
57
64
|
const candidate_article = await utils.dynamodb.get("candidate_articles", { candidate_id : "S6MI00392", article_id : "2025-08-1674855ce5e59c2b6a19a165037dffa5627b1cee071def07a614acb1c28d0e2eff" })
|
|
58
65
|
expect(candidate_article).toBeDefined()
|
|
59
66
|
if (candidate_article === undefined) {
|
|
@@ -62,7 +69,7 @@ describe("Testing Utils", () => {
|
|
|
62
69
|
expect(candidate_article.url).toBeDefined()
|
|
63
70
|
})
|
|
64
71
|
test("get_max", async () => {
|
|
65
|
-
const utils = new
|
|
72
|
+
const utils = new TriangleUtils(config)
|
|
66
73
|
const candidate_article = await utils.dynamodb.get_max("candidate_articles", { candidate_id : "S6MI00392" })
|
|
67
74
|
expect(candidate_article).toBeDefined()
|
|
68
75
|
if (candidate_article === undefined) {
|
|
@@ -73,7 +80,7 @@ describe("Testing Utils", () => {
|
|
|
73
80
|
expect(candidate_article.article_id.localeCompare("2025-08-1674855ce5e59c2b6a19a165037dffa5627b1cee071def07a614acb1c28d0e2eff") > 0).toBeTruthy()
|
|
74
81
|
})
|
|
75
82
|
test("query", async () => {
|
|
76
|
-
const utils = new
|
|
83
|
+
const utils = new TriangleUtils(config)
|
|
77
84
|
const candidate_articles = await utils.dynamodb.query("candidate_articles", { candidate_id : "S6MI00392" })
|
|
78
85
|
expect(candidate_articles).toBeDefined()
|
|
79
86
|
if (candidate_articles === undefined) {
|
|
@@ -81,5 +88,109 @@ describe("Testing Utils", () => {
|
|
|
81
88
|
}
|
|
82
89
|
expect(candidate_articles.length).toBeGreaterThan(400)
|
|
83
90
|
})
|
|
91
|
+
test("query_prefix", async () => {
|
|
92
|
+
const utils = new TriangleUtils(config)
|
|
93
|
+
const candidate_articles = await utils.dynamodb.query_prefix("candidate_articles", { candidate_id : "S6MI00392" }, { article_id : "2025-08-16"})
|
|
94
|
+
expect(candidate_articles).toBeDefined()
|
|
95
|
+
if (candidate_articles === undefined) {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
expect(candidate_articles.length).toBeGreaterThan(2)
|
|
99
|
+
expect(!candidate_articles.map(candidate_article => candidate_article.article_id.substring(0, 10) === "2025-08-16").includes(false)).toBeTruthy()
|
|
100
|
+
})
|
|
101
|
+
test("query_range", async () => {
|
|
102
|
+
const utils = new TriangleUtils(config)
|
|
103
|
+
const candidate_articles = await utils.dynamodb.query_range("candidate_articles", { candidate_id : "S6MI00392" }, { article_id : ["2025-08-01", "2025-08-31"] })
|
|
104
|
+
expect(candidate_articles).toBeDefined()
|
|
105
|
+
if (candidate_articles === undefined) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
expect(candidate_articles.length).toBeGreaterThan(10)
|
|
109
|
+
expect(!candidate_articles.map(candidate_article => candidate_article.article_id.substring(0, 7) === "2025-08").includes(false)).toBeTruthy()
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
describe("Testing S3", () => {
|
|
113
|
+
test("exists, get, create, delete, download_url, upload_url", async () => {
|
|
114
|
+
const utils = new TriangleUtils(config)
|
|
115
|
+
const s3_filename = "s3://triangleanalytics-raw-scrapes/test_file.txt"
|
|
116
|
+
await utils.s3.delete_file(s3_filename)
|
|
117
|
+
const file_exists_0 = await utils.s3.file_exists(s3_filename)
|
|
118
|
+
expect(file_exists_0).toBeFalsy()
|
|
119
|
+
await utils.s3.create_file(s3_filename, "test file contents.")
|
|
120
|
+
const file_exists_1 = await utils.s3.file_exists(s3_filename)
|
|
121
|
+
expect(file_exists_1).toBeTruthy()
|
|
122
|
+
const text = await utils.s3.get_file(s3_filename)
|
|
123
|
+
expect(text).toEqual("test file contents.")
|
|
124
|
+
const download_url = await utils.s3.generate_download_url(s3_filename)
|
|
125
|
+
expect(download_url).toBeDefined()
|
|
126
|
+
if (download_url === undefined) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
expect(download_url.includes("https://triangleanalytics-raw-scrapes.s3.us-east-1.amazonaws.com/test_file.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&")).toBeDefined()
|
|
130
|
+
const upload_url = await utils.s3.generate_upload_url(s3_filename)
|
|
131
|
+
expect(upload_url).toBeDefined()
|
|
132
|
+
await utils.s3.delete_file(s3_filename)
|
|
133
|
+
if (upload_url === undefined) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
expect(upload_url.includes("https://triangleanalytics-raw-scrapes.s3.us-east-1.amazonaws.com/test_file.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&")).toBeDefined()
|
|
137
|
+
const file_exists_2 = await utils.s3.file_exists(s3_filename)
|
|
138
|
+
expect(file_exists_2).toBeFalsy()
|
|
139
|
+
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
// describe("Testing Bee", () => {
|
|
143
|
+
// test("bee get page text", async () => {
|
|
144
|
+
// const utils = new Utils(config)
|
|
145
|
+
// const text = await utils.bee.get("https://marieforcongress.com/", { render_js : false, return_page_text : true })
|
|
146
|
+
// expect(text).toBeDefined()
|
|
147
|
+
// if (text === undefined) {
|
|
148
|
+
// return
|
|
149
|
+
// }
|
|
150
|
+
// expect(text.includes("Marie Gluesenkamp Perez’s working class roots run deep in Washington State.")).toBeTruthy()
|
|
151
|
+
// })
|
|
152
|
+
// test("bee google search", async () => {
|
|
153
|
+
// const utils = new Utils(config)
|
|
154
|
+
// const results = await utils.bee.google_search("Marie Gluesenkamp Perez", true)
|
|
155
|
+
// expect(results).toBeDefined()
|
|
156
|
+
// if (results === undefined) {
|
|
157
|
+
// return
|
|
158
|
+
// }
|
|
159
|
+
// expect(results.length).toBeGreaterThan(1)
|
|
160
|
+
// for (const result of results) {
|
|
161
|
+
// expect(result.link).toBeDefined()
|
|
162
|
+
// expect(result.date).toBeDefined()
|
|
163
|
+
// expect(result.title).toBeDefined()
|
|
164
|
+
// expect(result.source).toBeDefined()
|
|
165
|
+
// }
|
|
166
|
+
// })
|
|
167
|
+
// test("bee youtube search", async () => {
|
|
168
|
+
// const utils = new Utils(config)
|
|
169
|
+
// const results = await utils.bee.youtube_search("Marie Gluesenkamp Perez", true)
|
|
170
|
+
// expect(results).toBeDefined()
|
|
171
|
+
// if (results === undefined) {
|
|
172
|
+
// return
|
|
173
|
+
// }
|
|
174
|
+
// expect(results.length).toBeGreaterThan(1)
|
|
175
|
+
// for (const result of results) {
|
|
176
|
+
// expect(result.videoId).toBeDefined()
|
|
177
|
+
// }
|
|
178
|
+
// })
|
|
179
|
+
// })
|
|
180
|
+
describe("Testing Youtube", () => {
|
|
181
|
+
test("batch_get_videos", async () => {
|
|
182
|
+
const utils = new TriangleUtils(config)
|
|
183
|
+
const videos = await utils.youtube.batch_get_videos(["N4dyOzjjYYk", "rUyg3P1R5NQ"])
|
|
184
|
+
expect(videos).toBeDefined()
|
|
185
|
+
if (videos === undefined) {
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
expect(videos.length).toEqual(2)
|
|
189
|
+
for (const video of videos) {
|
|
190
|
+
expect(video.snippet !== undefined)
|
|
191
|
+
expect(video.contentDetails !== undefined)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
})
|
|
84
195
|
})
|
|
85
196
|
})
|
package/tsconfig.json
CHANGED
package/vite.config.ts
ADDED
package/dist/src/Utils.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Utils_Misc } from "./Utils_Misc.js";
|
|
2
|
-
import { Utils_DynamoDB } from "./Utils_DynamoDB.js";
|
|
3
|
-
import { Utils_S3 } from "./Utils_S3.js";
|
|
4
|
-
import { Utils_Bedrock } from "./Utils_Bedrock.js";
|
|
5
|
-
import { Utils_Bee } from "./Utils_Bee.js";
|
|
6
|
-
import { Utils_S3Vectors } from "./Utils_S3Vectors.js";
|
|
7
|
-
import { Utils_Youtube } from "./Utils_Youtube.js";
|
|
8
|
-
import { Config } from "./types/Config.js";
|
|
9
|
-
export declare class Utils extends Utils_Misc {
|
|
10
|
-
readonly dynamodb: Utils_DynamoDB;
|
|
11
|
-
readonly s3: Utils_S3;
|
|
12
|
-
readonly s3vectors: Utils_S3Vectors;
|
|
13
|
-
readonly bedrock: Utils_Bedrock;
|
|
14
|
-
readonly bee: Utils_Bee;
|
|
15
|
-
readonly youtube: Utils_Youtube;
|
|
16
|
-
constructor(config: Config);
|
|
17
|
-
}
|
package/dist/src/Utils.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Utils_Misc } from "./Utils_Misc.js";
|
|
2
|
-
import { Utils_DynamoDB } from "./Utils_DynamoDB.js";
|
|
3
|
-
import { Utils_S3 } from "./Utils_S3.js";
|
|
4
|
-
import { Utils_Bedrock } from "./Utils_Bedrock.js";
|
|
5
|
-
import { Utils_Bee } from "./Utils_Bee.js";
|
|
6
|
-
import { Utils_S3Vectors } from "./Utils_S3Vectors.js";
|
|
7
|
-
import { Utils_Youtube } from "./Utils_Youtube.js";
|
|
8
|
-
export class Utils extends Utils_Misc {
|
|
9
|
-
dynamodb;
|
|
10
|
-
s3;
|
|
11
|
-
s3vectors;
|
|
12
|
-
bedrock;
|
|
13
|
-
bee;
|
|
14
|
-
youtube;
|
|
15
|
-
constructor(config) {
|
|
16
|
-
super(config);
|
|
17
|
-
this.dynamodb = new Utils_DynamoDB(config);
|
|
18
|
-
this.s3 = new Utils_S3(config);
|
|
19
|
-
this.s3vectors = new Utils_S3Vectors(config);
|
|
20
|
-
this.bedrock = new Utils_Bedrock(config);
|
|
21
|
-
this.bee = new Utils_Bee(config);
|
|
22
|
-
this.youtube = new Utils_Youtube(config);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { BedrockRuntime } from "@aws-sdk/client-bedrock-runtime";
|
|
2
|
-
import { Config } from "./types/Config.js";
|
|
3
|
-
export declare class Utils_Bedrock {
|
|
4
|
-
readonly bedrock: BedrockRuntime;
|
|
5
|
-
readonly text_decoder: TextDecoder;
|
|
6
|
-
constructor(config: Config);
|
|
7
|
-
llama_invoke(model_id: string, prompt: string, temperature?: number, max_gen_len?: number, top_p?: number): Promise<string | undefined>;
|
|
8
|
-
gpt_converse(model_id: string, prompt: string, temperature?: number, max_gen_len?: number, top_p?: number): Promise<string | undefined>;
|
|
9
|
-
titan_invoke(text: string): Promise<string | undefined>;
|
|
10
|
-
}
|
package/dist/src/Utils_Bee.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { ScrapingBeeClient } from "scrapingbee";
|
|
2
|
-
import { Config } from "./types/Config.js";
|
|
3
|
-
export declare class Utils_Bee {
|
|
4
|
-
readonly config: Config;
|
|
5
|
-
readonly bee: ScrapingBeeClient | undefined;
|
|
6
|
-
readonly text_decoder: TextDecoder;
|
|
7
|
-
constructor(config: Config);
|
|
8
|
-
get(url: string, params?: Record<string, string>): Promise<string>;
|
|
9
|
-
google_search(query: string, news?: boolean): Promise<any>;
|
|
10
|
-
youtube_search(query: string, options?: {}): Promise<any>;
|
|
11
|
-
}
|
package/dist/src/Utils_Misc.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import * as nodemailer from "nodemailer";
|
|
2
|
-
import { Config } from "./types/Config.js";
|
|
3
|
-
interface Encryption {
|
|
4
|
-
iv: string;
|
|
5
|
-
ciphertext: string;
|
|
6
|
-
}
|
|
7
|
-
export declare class Utils_Misc {
|
|
8
|
-
readonly config: Config;
|
|
9
|
-
readonly text_encoder: TextEncoder;
|
|
10
|
-
readonly transporter: nodemailer.Transporter<import("nodemailer/lib/smtp-transport").SentMessageInfo, import("nodemailer/lib/smtp-transport").Options>;
|
|
11
|
-
constructor(config: Config);
|
|
12
|
-
wait(duration: number): Promise<unknown>;
|
|
13
|
-
get_current_time(): string;
|
|
14
|
-
get_current_milliseconds(): number;
|
|
15
|
-
send_email(recipient: string, subject: string, text: string): Promise<void>;
|
|
16
|
-
admin_alert(text: string): Promise<void>;
|
|
17
|
-
safe_run(f: () => Promise<any>): Promise<void>;
|
|
18
|
-
iterate<T>(inputs: T[], f: (input: T) => Promise<any>, concurrency?: number, print_indices?: boolean): Promise<void>;
|
|
19
|
-
sha256(input: string): Promise<string>;
|
|
20
|
-
encrypt(text: string): Encryption;
|
|
21
|
-
decrypt(encryption: Encryption): string;
|
|
22
|
-
get_secret_hash(username: string): string;
|
|
23
|
-
get_election_id(year: string, office: string, state: string, district: string): string | undefined;
|
|
24
|
-
get_chunk_indices(text_length: number, max_length?: number, overlap?: number): [number, number][];
|
|
25
|
-
chunkify(text: string, max_length?: number, overlap?: number): {
|
|
26
|
-
chunk_index: [number, number];
|
|
27
|
-
chunk_text: string;
|
|
28
|
-
}[];
|
|
29
|
-
}
|
|
30
|
-
export {};
|