@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
- count: number;
65
- total: number;
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 = Array.isArray(this.timestamps)
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(); // Ensure latest name list is loaded
103
- await this.loadCount(); // ✅ Load latest count before modifying
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; // ✅ Increment count correctly
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, // ✅ Store updated count properly
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
- const storedCount = await this.state.storage.get<number>('count');
126
- if (typeof storedCount === 'number') {
127
- this.count = storedCount; // ✅ Ensure we always load the latest count
128
- } else {
129
- this.count = 0; // ✅ Ensure count is initialized properly
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(@inject(ConceptService) private conceptService: ConceptService) {}
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?: boolean
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; // Adjust based on response speed
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
- combinations,
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); // Store failed ones
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); // Retry
141
+ await this.processBatch(conceptSlug, failedCombinations, phase, override);
94
142
  }
143
+
95
144
  console.log(
96
- `✅ Batch processing completed for concept: ${conceptSlug}, Phase: ${phase}`
145
+ `🎉 All combinations successfully processed for ${conceptSlug}!`
97
146
  );
98
147
  }
99
148
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.182",
3
+ "version": "0.0.183",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -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';