@zodic/shared 0.0.182 → 0.0.184
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.
|
@@ -6,6 +6,7 @@ export class ConceptNameDO {
|
|
|
6
6
|
names: { 'en-us': string[]; 'pt-br': string[] };
|
|
7
7
|
count: number;
|
|
8
8
|
timestamps: number[];
|
|
9
|
+
lastIndex: number; // ✅ Tracks last processed index
|
|
9
10
|
|
|
10
11
|
static TOTAL_NAMES = 1728; // Maximum total names to be generated
|
|
11
12
|
|
|
@@ -15,12 +16,15 @@ export class ConceptNameDO {
|
|
|
15
16
|
this.names = { 'en-us': [], 'pt-br': [] };
|
|
16
17
|
this.count = 0;
|
|
17
18
|
this.timestamps = [];
|
|
19
|
+
this.lastIndex = 0; // ✅ Initialize lastIndex
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
async fetch(request: Request): Promise<Response> {
|
|
21
23
|
const url = new URL(request.url);
|
|
22
24
|
const method = request.method;
|
|
23
25
|
|
|
26
|
+
console.log(`📥 DO Received Request: ${method} ${url.pathname}`);
|
|
27
|
+
|
|
24
28
|
if (method === 'GET' && url.pathname === '/names') {
|
|
25
29
|
return new Response(JSON.stringify(await this.getNames()), {
|
|
26
30
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -33,17 +37,30 @@ export class ConceptNameDO {
|
|
|
33
37
|
});
|
|
34
38
|
}
|
|
35
39
|
|
|
40
|
+
if (method === 'GET' && url.pathname === '/last-index') {
|
|
41
|
+
return new Response(JSON.stringify(await this.getLastIndex()), {
|
|
42
|
+
headers: { 'Content-Type': 'application/json' },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
if (method === 'POST' && url.pathname === '/add-name') {
|
|
37
|
-
const { language, name } = (await request.json()) as {
|
|
47
|
+
const { language, name, index } = (await request.json()) as {
|
|
38
48
|
language: 'en-us' | 'pt-br';
|
|
39
49
|
name: string;
|
|
50
|
+
index: number;
|
|
40
51
|
};
|
|
41
52
|
|
|
42
53
|
if (!['en-us', 'pt-br'].includes(language)) {
|
|
43
54
|
return new Response('Invalid language', { status: 400 });
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
await this.addName(language, name);
|
|
57
|
+
await this.addName(language, name, index);
|
|
58
|
+
return new Response('OK', { status: 200 });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (method === 'POST' && url.pathname === '/set-progress') {
|
|
62
|
+
const { lastIndex } = (await request.json()) as { lastIndex: number };
|
|
63
|
+
await this.setLastIndex(lastIndex);
|
|
47
64
|
return new Response('OK', { status: 200 });
|
|
48
65
|
}
|
|
49
66
|
|
|
@@ -63,25 +80,19 @@ export class ConceptNameDO {
|
|
|
63
80
|
async getProgress(): Promise<{
|
|
64
81
|
count: number;
|
|
65
82
|
total: number;
|
|
83
|
+
lastIndex: number;
|
|
66
84
|
progress: string;
|
|
67
85
|
ratePerMinute: number;
|
|
68
86
|
estimatedTimeMinutes: number | 'N/A';
|
|
69
87
|
}> {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!Array.isArray(this.timestamps) || this.timestamps.length === 0) {
|
|
74
|
-
this.timestamps =
|
|
75
|
-
(await this.state.storage.get<number[]>('timestamps')) || [];
|
|
76
|
-
}
|
|
88
|
+
await this.loadCount();
|
|
89
|
+
await this.loadTimestamps();
|
|
90
|
+
await this.loadLastIndex(); // ✅ Ensure latest lastIndex is fetched
|
|
77
91
|
|
|
78
92
|
const now = Date.now();
|
|
79
93
|
const oneMinuteAgo = now - 60 * 1000;
|
|
80
94
|
|
|
81
|
-
const recentTimestamps =
|
|
82
|
-
? this.timestamps.filter((ts) => ts >= oneMinuteAgo)
|
|
83
|
-
: []; // ✅ Ensure it's an array before calling filter
|
|
84
|
-
|
|
95
|
+
const recentTimestamps = this.timestamps.filter((ts) => ts >= oneMinuteAgo);
|
|
85
96
|
const ratePerMinute = recentTimestamps.length;
|
|
86
97
|
|
|
87
98
|
const remainingNames = ConceptNameDO.TOTAL_NAMES - this.count;
|
|
@@ -91,6 +102,7 @@ export class ConceptNameDO {
|
|
|
91
102
|
return {
|
|
92
103
|
count: this.count,
|
|
93
104
|
total: ConceptNameDO.TOTAL_NAMES,
|
|
105
|
+
lastIndex: this.lastIndex, // ✅ Include lastIndex in response
|
|
94
106
|
progress:
|
|
95
107
|
((this.count / ConceptNameDO.TOTAL_NAMES) * 100).toFixed(2) + '%',
|
|
96
108
|
ratePerMinute,
|
|
@@ -98,50 +110,72 @@ export class ConceptNameDO {
|
|
|
98
110
|
};
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
async
|
|
102
|
-
await this.
|
|
103
|
-
|
|
113
|
+
async getLastIndex(): Promise<number> {
|
|
114
|
+
await this.loadLastIndex(); // ✅ Fetch lastIndex from storage
|
|
115
|
+
return this.lastIndex;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async addName(
|
|
119
|
+
language: 'en-us' | 'pt-br',
|
|
120
|
+
name: string,
|
|
121
|
+
index: number
|
|
122
|
+
): Promise<void> {
|
|
123
|
+
await this.getNames();
|
|
124
|
+
await this.loadCount();
|
|
125
|
+
await this.loadLastIndex(); // ✅ Ensure lastIndex is loaded before modifying
|
|
104
126
|
|
|
105
127
|
if (!this.names[language].includes(name)) {
|
|
106
128
|
this.names[language].push(name);
|
|
107
|
-
this.count += 1;
|
|
129
|
+
this.count += 1;
|
|
108
130
|
|
|
109
131
|
this.recordTimestamp();
|
|
110
132
|
|
|
111
|
-
//
|
|
133
|
+
// 🔥 **Ensure lastIndex is updated**
|
|
134
|
+
if (index > this.lastIndex) {
|
|
135
|
+
this.lastIndex = index;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 🔥 **Store all updated values in a single put() call**
|
|
112
139
|
await this.state.storage.put({
|
|
113
140
|
names: this.names,
|
|
114
|
-
count: this.count,
|
|
141
|
+
count: this.count,
|
|
115
142
|
timestamps: this.timestamps,
|
|
143
|
+
lastIndex: this.lastIndex, // ✅ Store the last processed index
|
|
116
144
|
});
|
|
117
145
|
|
|
118
146
|
console.log(
|
|
119
|
-
`✅ Added name "${name}" to ${language}, count
|
|
147
|
+
`✅ Added name "${name}" to ${language}, count: ${this.count}, lastIndex: ${this.lastIndex}`
|
|
120
148
|
);
|
|
121
149
|
}
|
|
122
150
|
}
|
|
123
151
|
|
|
152
|
+
async setLastIndex(lastIndex: number): Promise<void> {
|
|
153
|
+
this.lastIndex = lastIndex;
|
|
154
|
+
await this.state.storage.put('lastIndex', this.lastIndex);
|
|
155
|
+
console.log(`📌 Updated lastIndex to ${this.lastIndex}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
124
158
|
async loadCount(): Promise<void> {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
159
|
+
this.count = (await this.state.storage.get<number>('count')) || 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async loadLastIndex(): Promise<void> {
|
|
163
|
+
const storedIndex = await this.state.storage.get<number>('lastIndex');
|
|
164
|
+
if (typeof storedIndex === 'number') {
|
|
165
|
+
this.lastIndex = storedIndex;
|
|
128
166
|
} else {
|
|
129
|
-
this.
|
|
167
|
+
this.lastIndex = 0; // ✅ Ensure it starts from 0
|
|
130
168
|
}
|
|
131
169
|
}
|
|
132
170
|
|
|
171
|
+
async loadTimestamps(): Promise<void> {
|
|
172
|
+
this.timestamps =
|
|
173
|
+
(await this.state.storage.get<number[]>('timestamps')) || [];
|
|
174
|
+
}
|
|
175
|
+
|
|
133
176
|
recordTimestamp(): void {
|
|
134
177
|
const now = Date.now();
|
|
135
|
-
|
|
136
|
-
// ✅ Ensure timestamps is always an array before modifying
|
|
137
|
-
if (!Array.isArray(this.timestamps)) {
|
|
138
|
-
this.timestamps = [];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ✅ Keep only the last 60 timestamps
|
|
142
|
-
if (this.timestamps.length >= 60) {
|
|
143
|
-
this.timestamps.splice(0, this.timestamps.length - 59);
|
|
144
|
-
}
|
|
178
|
+
this.timestamps = this.timestamps.filter((ts) => now - ts < 60 * 60 * 1000);
|
|
145
179
|
this.timestamps.push(now);
|
|
146
180
|
}
|
|
147
181
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { inject, injectable } from 'inversify';
|
|
2
2
|
import pMap from 'p-map';
|
|
3
3
|
import { zodiacSignCombinations } from '../../data/zodiacSignCombinations';
|
|
4
|
-
import { Concept, ConceptPhase } from '../../types';
|
|
4
|
+
import { Concept, ConceptPhase, ConceptProgress } from '../../types';
|
|
5
|
+
import { AppContext } from '../base';
|
|
5
6
|
import { ConceptService } from '../services/ConceptService';
|
|
6
7
|
|
|
7
8
|
@injectable()
|
|
8
9
|
export class ConceptWorkflow {
|
|
9
|
-
constructor(
|
|
10
|
+
constructor(
|
|
11
|
+
@inject(AppContext) private context: AppContext,
|
|
12
|
+
@inject(ConceptService) private conceptService: ConceptService
|
|
13
|
+
) {}
|
|
10
14
|
|
|
11
15
|
async processSingle(
|
|
12
16
|
conceptSlug: Concept,
|
|
@@ -66,34 +70,79 @@ export class ConceptWorkflow {
|
|
|
66
70
|
conceptSlug: Concept,
|
|
67
71
|
combinations: string[],
|
|
68
72
|
phase: ConceptPhase,
|
|
69
|
-
override
|
|
73
|
+
override: boolean = false
|
|
70
74
|
) {
|
|
71
75
|
console.log(
|
|
72
76
|
`🚀 Processing batch for concept: ${conceptSlug}, Phase: ${phase}, Total Combinations: ${combinations.length}`
|
|
73
77
|
);
|
|
74
78
|
|
|
75
|
-
const concurrency =
|
|
79
|
+
const concurrency = 5;
|
|
76
80
|
const failedCombinations: string[] = [];
|
|
77
81
|
|
|
82
|
+
// ✅ Get Durable Object stub for progress tracking
|
|
83
|
+
const id = this.context.env.CONCEPT_NAMES_DO.idFromName(conceptSlug);
|
|
84
|
+
const stub = this.context.env.CONCEPT_NAMES_DO.get(id);
|
|
85
|
+
|
|
86
|
+
// ✅ Try to load last checkpoint from DO
|
|
87
|
+
let lastIndex = 0;
|
|
88
|
+
try {
|
|
89
|
+
const progressRes = await stub.fetch(`https://internal/progress`);
|
|
90
|
+
if (progressRes.ok) {
|
|
91
|
+
const progressData = (await progressRes.json()) as ConceptProgress;
|
|
92
|
+
lastIndex = progressData.lastIndex || 0;
|
|
93
|
+
console.log(`🔄 Resuming from index ${lastIndex}`);
|
|
94
|
+
} else {
|
|
95
|
+
console.log(`📌 No previous checkpoint found, starting from scratch.`);
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(`❌ Error fetching progress from DO:`, error);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ✅ Filter unprocessed combinations
|
|
102
|
+
const remainingCombinations = combinations.slice(lastIndex);
|
|
103
|
+
console.log(
|
|
104
|
+
`⏳ Processing ${remainingCombinations.length} remaining combinations...`
|
|
105
|
+
);
|
|
106
|
+
|
|
78
107
|
await pMap(
|
|
79
|
-
|
|
80
|
-
async (combination) => {
|
|
108
|
+
remainingCombinations,
|
|
109
|
+
async (combination, index) => {
|
|
110
|
+
const currentIndex = lastIndex + index;
|
|
81
111
|
try {
|
|
112
|
+
console.log(
|
|
113
|
+
`🛠️ Processing combination ${currentIndex}/${combinations.length}: ${combination}`
|
|
114
|
+
);
|
|
82
115
|
await this.processSingle(conceptSlug, combination, phase, override);
|
|
116
|
+
|
|
117
|
+
// ✅ Save progress every 10 iterations in DO
|
|
118
|
+
if (currentIndex % 10 === 0) {
|
|
119
|
+
await stub.fetch(`https://internal/set-progress`, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
body: JSON.stringify({ lastIndex: currentIndex }),
|
|
122
|
+
headers: { 'Content-Type': 'application/json' },
|
|
123
|
+
});
|
|
124
|
+
console.log(`✅ Checkpoint saved at index ${currentIndex}`);
|
|
125
|
+
}
|
|
83
126
|
} catch (error) {
|
|
84
127
|
console.error(`❌ Error processing ${combination}:`, error);
|
|
85
|
-
failedCombinations.push(combination);
|
|
128
|
+
failedCombinations.push(combination);
|
|
86
129
|
}
|
|
87
130
|
},
|
|
88
131
|
{ concurrency }
|
|
89
132
|
);
|
|
90
133
|
|
|
134
|
+
console.log(
|
|
135
|
+
`✅ Batch processing completed for ${conceptSlug}, Phase: ${phase}`
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// ✅ Retry failed combinations after all batches
|
|
91
139
|
if (failedCombinations.length > 0) {
|
|
92
140
|
console.warn(`⚠️ Retrying failed combinations:`, failedCombinations);
|
|
93
|
-
await this.processBatch(conceptSlug, failedCombinations, phase, override);
|
|
141
|
+
await this.processBatch(conceptSlug, failedCombinations, phase, override);
|
|
94
142
|
}
|
|
143
|
+
|
|
95
144
|
console.log(
|
|
96
|
-
|
|
145
|
+
`🎉 All combinations successfully processed for ${conceptSlug}!`
|
|
97
146
|
);
|
|
98
147
|
}
|
|
99
148
|
|
package/package.json
CHANGED
package/types/scopes/generic.ts
CHANGED
|
@@ -402,6 +402,7 @@ export type AstroKVData = {
|
|
|
402
402
|
export type ConceptProgress = {
|
|
403
403
|
count: number;
|
|
404
404
|
total: number;
|
|
405
|
+
lastIndex: number;
|
|
405
406
|
progress: string; // Percentage as string (e.g., "75.32%")
|
|
406
407
|
ratePerMinute: number;
|
|
407
408
|
estimatedTimeMinutes: number | 'N/A';
|