@zodic/shared 0.0.182 → 0.0.183
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.
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DurableObjectState } from '@cloudflare/workers-types';
|
|
2
|
+
import { ConceptProgress } from '../../types';
|
|
2
3
|
|
|
3
4
|
export class ConceptNameDO {
|
|
4
5
|
state: DurableObjectState;
|
|
@@ -6,6 +7,7 @@ export class ConceptNameDO {
|
|
|
6
7
|
names: { 'en-us': string[]; 'pt-br': string[] };
|
|
7
8
|
count: number;
|
|
8
9
|
timestamps: number[];
|
|
10
|
+
lastIndex: number; // ✅ Stores last processed index for batch processing
|
|
9
11
|
|
|
10
12
|
static TOTAL_NAMES = 1728; // Maximum total names to be generated
|
|
11
13
|
|
|
@@ -15,6 +17,7 @@ export class ConceptNameDO {
|
|
|
15
17
|
this.names = { 'en-us': [], 'pt-br': [] };
|
|
16
18
|
this.count = 0;
|
|
17
19
|
this.timestamps = [];
|
|
20
|
+
this.lastIndex = 0; // ✅ Initialize lastIndex
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
async fetch(request: Request): Promise<Response> {
|
|
@@ -33,6 +36,12 @@ export class ConceptNameDO {
|
|
|
33
36
|
});
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
if (method === 'GET' && url.pathname === '/last-index') {
|
|
40
|
+
return new Response(JSON.stringify(await this.getLastIndex()), {
|
|
41
|
+
headers: { 'Content-Type': 'application/json' },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
if (method === 'POST' && url.pathname === '/add-name') {
|
|
37
46
|
const { language, name } = (await request.json()) as {
|
|
38
47
|
language: 'en-us' | 'pt-br';
|
|
@@ -47,6 +56,12 @@ export class ConceptNameDO {
|
|
|
47
56
|
return new Response('OK', { status: 200 });
|
|
48
57
|
}
|
|
49
58
|
|
|
59
|
+
if (method === 'POST' && url.pathname === '/set-progress') {
|
|
60
|
+
const { lastIndex } = (await request.json()) as { lastIndex: number };
|
|
61
|
+
await this.setLastIndex(lastIndex);
|
|
62
|
+
return new Response('OK', { status: 200 });
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
return new Response('Not Found', { status: 404 });
|
|
51
66
|
}
|
|
52
67
|
|
|
@@ -60,28 +75,14 @@ export class ConceptNameDO {
|
|
|
60
75
|
return this.names;
|
|
61
76
|
}
|
|
62
77
|
|
|
63
|
-
async getProgress(): Promise<{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
progress: string;
|
|
67
|
-
ratePerMinute: number;
|
|
68
|
-
estimatedTimeMinutes: number | 'N/A';
|
|
69
|
-
}> {
|
|
70
|
-
if (this.count === 0) {
|
|
71
|
-
this.count = (await this.state.storage.get<number>('count')) || 0;
|
|
72
|
-
}
|
|
73
|
-
if (!Array.isArray(this.timestamps) || this.timestamps.length === 0) {
|
|
74
|
-
this.timestamps =
|
|
75
|
-
(await this.state.storage.get<number[]>('timestamps')) || [];
|
|
76
|
-
}
|
|
78
|
+
async getProgress(): Promise<ConceptProgress> {
|
|
79
|
+
await this.loadCount();
|
|
80
|
+
await this.loadTimestamps();
|
|
77
81
|
|
|
78
82
|
const now = Date.now();
|
|
79
83
|
const oneMinuteAgo = now - 60 * 1000;
|
|
80
84
|
|
|
81
|
-
const recentTimestamps =
|
|
82
|
-
? this.timestamps.filter((ts) => ts >= oneMinuteAgo)
|
|
83
|
-
: []; // ✅ Ensure it's an array before calling filter
|
|
84
|
-
|
|
85
|
+
const recentTimestamps = this.timestamps.filter((ts) => ts >= oneMinuteAgo);
|
|
85
86
|
const ratePerMinute = recentTimestamps.length;
|
|
86
87
|
|
|
87
88
|
const remainingNames = ConceptNameDO.TOTAL_NAMES - this.count;
|
|
@@ -91,6 +92,7 @@ export class ConceptNameDO {
|
|
|
91
92
|
return {
|
|
92
93
|
count: this.count,
|
|
93
94
|
total: ConceptNameDO.TOTAL_NAMES,
|
|
95
|
+
lastIndex: this.lastIndex,
|
|
94
96
|
progress:
|
|
95
97
|
((this.count / ConceptNameDO.TOTAL_NAMES) * 100).toFixed(2) + '%',
|
|
96
98
|
ratePerMinute,
|
|
@@ -98,20 +100,25 @@ export class ConceptNameDO {
|
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
102
|
|
|
103
|
+
async getLastIndex(): Promise<number> {
|
|
104
|
+
if (this.lastIndex === 0) {
|
|
105
|
+
this.lastIndex = (await this.state.storage.get<number>('lastIndex')) || 0;
|
|
106
|
+
}
|
|
107
|
+
return this.lastIndex;
|
|
108
|
+
}
|
|
109
|
+
|
|
101
110
|
async addName(language: 'en-us' | 'pt-br', name: string): Promise<void> {
|
|
102
|
-
await this.getNames();
|
|
103
|
-
await this.loadCount();
|
|
111
|
+
await this.getNames();
|
|
112
|
+
await this.loadCount();
|
|
104
113
|
|
|
105
114
|
if (!this.names[language].includes(name)) {
|
|
106
115
|
this.names[language].push(name);
|
|
107
|
-
this.count += 1;
|
|
108
|
-
|
|
116
|
+
this.count += 1;
|
|
109
117
|
this.recordTimestamp();
|
|
110
118
|
|
|
111
|
-
// ✅ **Store all updated values in a single put() call**
|
|
112
119
|
await this.state.storage.put({
|
|
113
120
|
names: this.names,
|
|
114
|
-
count: this.count,
|
|
121
|
+
count: this.count,
|
|
115
122
|
timestamps: this.timestamps,
|
|
116
123
|
});
|
|
117
124
|
|
|
@@ -121,27 +128,23 @@ export class ConceptNameDO {
|
|
|
121
128
|
}
|
|
122
129
|
}
|
|
123
130
|
|
|
131
|
+
async setLastIndex(lastIndex: number): Promise<void> {
|
|
132
|
+
this.lastIndex = lastIndex;
|
|
133
|
+
await this.state.storage.put('lastIndex', this.lastIndex);
|
|
134
|
+
}
|
|
135
|
+
|
|
124
136
|
async loadCount(): Promise<void> {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
this.count = (await this.state.storage.get<number>('count')) || 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async loadTimestamps(): Promise<void> {
|
|
141
|
+
this.timestamps =
|
|
142
|
+
(await this.state.storage.get<number[]>('timestamps')) || [];
|
|
131
143
|
}
|
|
132
144
|
|
|
133
145
|
recordTimestamp(): void {
|
|
134
146
|
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
|
-
}
|
|
147
|
+
this.timestamps = this.timestamps.filter((ts) => now - ts < 60 * 60 * 1000);
|
|
145
148
|
this.timestamps.push(now);
|
|
146
149
|
}
|
|
147
150
|
}
|
|
@@ -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 = 20;
|
|
79
|
+
const concurrency = 20;
|
|
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';
|