@zodic/shared 0.0.236 → 0.0.238
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/app/services/ConceptService.ts +259 -24
- package/db/schema.ts +62 -0
- package/package.json +1 -1
- package/types/scopes/legacy.ts +100 -12
- package/utils/astroPrompts/index.ts +91 -0
- package/utils/astroPrompts/pointNameMap.ts +29 -0
- package/utils/index.ts +10 -1
|
@@ -550,22 +550,20 @@ export class ConceptService {
|
|
|
550
550
|
structuredContentEN: StructuredConceptContent;
|
|
551
551
|
structuredContentPT: StructuredConceptContent;
|
|
552
552
|
}> {
|
|
553
|
-
console.log(
|
|
554
|
-
`📌 [START] Parsing structured content for ${conceptSlug} from ChatGPT response.`
|
|
555
|
-
);
|
|
553
|
+
console.log(`📌 [START] Parsing structured content for ${conceptSlug}`);
|
|
556
554
|
|
|
557
555
|
// ✅ Step 1: Clean artifacts before processing
|
|
558
556
|
let cleanedResponse = response
|
|
559
557
|
.replace(/[*#•]/g, '') // Remove bullet points, bold markers, headings
|
|
560
558
|
.replace(/---+/g, '') // Remove dividers (like "---")
|
|
561
559
|
.replace(/\n\s*\n/g, '\n\n') // Normalize multiple line breaks
|
|
562
|
-
.replace(/\s+EN:\s*/g, '\nEN: ') //
|
|
563
|
-
.replace(/\s+PT:\s*/g, '\nPT: ') //
|
|
560
|
+
.replace(/\s+EN:\s*/g, '\nEN: ') // Normalize "EN:"
|
|
561
|
+
.replace(/\s+PT:\s*/g, '\nPT: ') // Normalize "PT:"
|
|
564
562
|
.trim();
|
|
565
563
|
|
|
566
564
|
console.log(
|
|
567
565
|
'🔹 [CLEANED RESPONSE]:',
|
|
568
|
-
cleanedResponse.slice(0,
|
|
566
|
+
cleanedResponse.slice(0, 3000) + '...'
|
|
569
567
|
);
|
|
570
568
|
|
|
571
569
|
try {
|
|
@@ -677,12 +675,13 @@ export class ConceptService {
|
|
|
677
675
|
}
|
|
678
676
|
|
|
679
677
|
// ✅ Step 3: Detect if AI interleaved EN/PT sections
|
|
680
|
-
const interleavedMatches = cleanedResponse.match(
|
|
681
|
-
/(EN:\s*[\s\S]+?PT:\s*[\s\S]+?)(?=EN:|$)/
|
|
682
|
-
);
|
|
683
678
|
let extractedEN = '';
|
|
684
679
|
let extractedPT = '';
|
|
685
680
|
|
|
681
|
+
const interleavedMatches = cleanedResponse.match(
|
|
682
|
+
/(EN:\s*[\s\S]+?PT:\s*[\s\S]+?)(?=EN:|$)/g
|
|
683
|
+
);
|
|
684
|
+
|
|
686
685
|
if (interleavedMatches) {
|
|
687
686
|
console.warn(
|
|
688
687
|
'⚠️ Detected interleaved EN/PT sections. Parsing accordingly.'
|
|
@@ -706,21 +705,21 @@ export class ConceptService {
|
|
|
706
705
|
}
|
|
707
706
|
|
|
708
707
|
console.log(
|
|
709
|
-
'✅
|
|
710
|
-
extractedEN.slice(0,
|
|
708
|
+
'✅ Extracted English Content:',
|
|
709
|
+
extractedEN.slice(0, 1000) + '...'
|
|
711
710
|
);
|
|
712
711
|
console.log(
|
|
713
|
-
'✅
|
|
714
|
-
extractedPT.slice(0,
|
|
712
|
+
'✅ Extracted Portuguese Content:',
|
|
713
|
+
extractedPT.slice(0, 1000) + '...'
|
|
715
714
|
);
|
|
716
715
|
|
|
717
716
|
// ✅ Step 4: Extract structured sections dynamically
|
|
718
717
|
function extractSections(text: string, sectionTitles: string[]) {
|
|
719
718
|
return sectionTitles.map((title) => {
|
|
720
|
-
console.log(`🔍
|
|
719
|
+
console.log(`🔍 Extracting section: "${title}"`);
|
|
721
720
|
|
|
722
721
|
const regex = new RegExp(
|
|
723
|
-
|
|
722
|
+
`(?:\\d+\\.\\s*)?${title}\\s*:?\\s*([\\s\\S]+?)(?=\\n(?:\\d+\\.\\s*)?[A-Z]|$)`
|
|
724
723
|
);
|
|
725
724
|
const match = text.match(regex);
|
|
726
725
|
|
|
@@ -734,10 +733,7 @@ export class ConceptService {
|
|
|
734
733
|
.map((p) => p.trim())
|
|
735
734
|
.filter((p) => p.length > 0);
|
|
736
735
|
|
|
737
|
-
console.log(
|
|
738
|
-
`✅ [EXTRACTED] "${title}" - Parsed Content:`,
|
|
739
|
-
paragraphs
|
|
740
|
-
);
|
|
736
|
+
console.log(`✅ Extracted "${title}" - Parsed Content:`, paragraphs);
|
|
741
737
|
return { type: 'section', title, content: paragraphs };
|
|
742
738
|
});
|
|
743
739
|
}
|
|
@@ -746,17 +742,15 @@ export class ConceptService {
|
|
|
746
742
|
const structuredContentPT = extractSections(extractedPT, sectionsPT);
|
|
747
743
|
|
|
748
744
|
console.log(
|
|
749
|
-
'🎯
|
|
745
|
+
'🎯 Parsed English Content:',
|
|
750
746
|
JSON.stringify(structuredContentEN, null, 2)
|
|
751
747
|
);
|
|
752
748
|
console.log(
|
|
753
|
-
'🎯
|
|
749
|
+
'🎯 Parsed Portuguese Content:',
|
|
754
750
|
JSON.stringify(structuredContentPT, null, 2)
|
|
755
751
|
);
|
|
756
752
|
|
|
757
|
-
console.log(
|
|
758
|
-
'✅ [COMPLETE] Structured content parsing finished successfully.'
|
|
759
|
-
);
|
|
753
|
+
console.log('✅ Structured content parsing finished successfully.');
|
|
760
754
|
|
|
761
755
|
return {
|
|
762
756
|
structuredContentEN,
|
|
@@ -784,6 +778,247 @@ export class ConceptService {
|
|
|
784
778
|
}
|
|
785
779
|
}
|
|
786
780
|
|
|
781
|
+
// async parseStructuredContent(
|
|
782
|
+
// conceptSlug: Concept,
|
|
783
|
+
// response: string
|
|
784
|
+
// ): Promise<{
|
|
785
|
+
// structuredContentEN: StructuredConceptContent;
|
|
786
|
+
// structuredContentPT: StructuredConceptContent;
|
|
787
|
+
// }> {
|
|
788
|
+
// console.log(
|
|
789
|
+
// `📌 [START] Parsing structured content for ${conceptSlug} from ChatGPT response.`
|
|
790
|
+
// );
|
|
791
|
+
|
|
792
|
+
// // ✅ Step 1: Clean artifacts before processing
|
|
793
|
+
// let cleanedResponse = response
|
|
794
|
+
// .replace(/[*#•]/g, '') // Remove bullet points, bold markers, headings
|
|
795
|
+
// .replace(/---+/g, '') // Remove dividers (like "---")
|
|
796
|
+
// .replace(/\n\s*\n/g, '\n\n') // Normalize multiple line breaks
|
|
797
|
+
// .replace(/\s+EN:\s*/g, '\nEN: ') // Ensure consistent "EN:" formatting
|
|
798
|
+
// .replace(/\s+PT:\s*/g, '\nPT: ') // Ensure consistent "PT:" formatting
|
|
799
|
+
// .trim();
|
|
800
|
+
|
|
801
|
+
// console.log(
|
|
802
|
+
// '🔹 [CLEANED RESPONSE]:',
|
|
803
|
+
// cleanedResponse.slice(0, 500) + '...'
|
|
804
|
+
// );
|
|
805
|
+
|
|
806
|
+
// try {
|
|
807
|
+
// // ✅ Step 2: Define Section Titles Per Concept
|
|
808
|
+
// const conceptSections: Record<string, { en: string[]; pt: string[] }> = {
|
|
809
|
+
// crown: {
|
|
810
|
+
// en: [
|
|
811
|
+
// 'Core Identity',
|
|
812
|
+
// 'Strengths and Challenges',
|
|
813
|
+
// 'Path to Fulfillment',
|
|
814
|
+
// 'Emotional Depth',
|
|
815
|
+
// 'Vision and Aspirations',
|
|
816
|
+
// ],
|
|
817
|
+
// pt: [
|
|
818
|
+
// 'Identidade Essencial',
|
|
819
|
+
// 'Forças e Desafios',
|
|
820
|
+
// 'Caminho para a Plenitude',
|
|
821
|
+
// 'Profundidade Emocional',
|
|
822
|
+
// 'Visão e Aspirações',
|
|
823
|
+
// ],
|
|
824
|
+
// },
|
|
825
|
+
// scepter: {
|
|
826
|
+
// en: [
|
|
827
|
+
// 'The Power of Expression',
|
|
828
|
+
// 'The Dance of Action and Reaction',
|
|
829
|
+
// 'Navigating Challenges',
|
|
830
|
+
// 'The Mirror of Relations',
|
|
831
|
+
// 'Impact on the World',
|
|
832
|
+
// ],
|
|
833
|
+
// pt: [
|
|
834
|
+
// 'O Poder da Expressão',
|
|
835
|
+
// 'A Dança da Ação e Reação',
|
|
836
|
+
// 'Navegando Desafios',
|
|
837
|
+
// 'O Espelho das Relações',
|
|
838
|
+
// 'Impacto no Mundo',
|
|
839
|
+
// ],
|
|
840
|
+
// },
|
|
841
|
+
// amulet: {
|
|
842
|
+
// en: [
|
|
843
|
+
// 'The Shield of Protection',
|
|
844
|
+
// 'The Strength Within',
|
|
845
|
+
// 'Nurturing Growth',
|
|
846
|
+
// 'The Cycle of Renewal',
|
|
847
|
+
// 'The Anchor of Wisdom',
|
|
848
|
+
// ],
|
|
849
|
+
// pt: [
|
|
850
|
+
// 'O Escudo da Proteção',
|
|
851
|
+
// 'A Força Interior',
|
|
852
|
+
// 'Cultivando o Crescimento',
|
|
853
|
+
// 'O Ciclo da Renovação',
|
|
854
|
+
// 'A Âncora da Sabedoria',
|
|
855
|
+
// ],
|
|
856
|
+
// },
|
|
857
|
+
// ring: {
|
|
858
|
+
// en: [
|
|
859
|
+
// 'Magnetic Pull',
|
|
860
|
+
// 'Hidden Desires',
|
|
861
|
+
// 'The Fated Dance',
|
|
862
|
+
// 'Unveiling the Shadows',
|
|
863
|
+
// 'Your Love Power',
|
|
864
|
+
// ],
|
|
865
|
+
// pt: [
|
|
866
|
+
// 'Força Magnética',
|
|
867
|
+
// 'Desejos Ocultos',
|
|
868
|
+
// 'A Dança do Destino',
|
|
869
|
+
// 'Revelando as Sombras',
|
|
870
|
+
// 'Seu Poder no Amor',
|
|
871
|
+
// ],
|
|
872
|
+
// },
|
|
873
|
+
// lantern: {
|
|
874
|
+
// en: [
|
|
875
|
+
// 'The Call to Awakening',
|
|
876
|
+
// 'Walking Through Shadows',
|
|
877
|
+
// 'The Inner Flame',
|
|
878
|
+
// 'Revelations and Truths',
|
|
879
|
+
// 'Becoming the Light',
|
|
880
|
+
// ],
|
|
881
|
+
// pt: [
|
|
882
|
+
// 'O Chamado para o Despertar',
|
|
883
|
+
// 'Caminhando Pelas Sombras',
|
|
884
|
+
// 'A Chama Interior',
|
|
885
|
+
// 'Revelações e Verdades',
|
|
886
|
+
// 'Tornando-se a Luz',
|
|
887
|
+
// ],
|
|
888
|
+
// },
|
|
889
|
+
// orb: {
|
|
890
|
+
// en: [
|
|
891
|
+
// 'The Path Unfolding',
|
|
892
|
+
// 'A Calling Beyond the Self',
|
|
893
|
+
// 'Turning Points of Fate',
|
|
894
|
+
// 'Mastering the Journey',
|
|
895
|
+
// 'Legacy and Impact',
|
|
896
|
+
// ],
|
|
897
|
+
// pt: [
|
|
898
|
+
// 'O Caminho que se Revela',
|
|
899
|
+
// 'Um Chamado Além de Si Mesmo',
|
|
900
|
+
// 'Pontos de Virada do Destino',
|
|
901
|
+
// 'Dominando a Jornada',
|
|
902
|
+
// 'Legado e Impacto',
|
|
903
|
+
// ],
|
|
904
|
+
// },
|
|
905
|
+
// };
|
|
906
|
+
|
|
907
|
+
// const sectionsEN = conceptSections[conceptSlug]?.en;
|
|
908
|
+
// const sectionsPT = conceptSections[conceptSlug]?.pt;
|
|
909
|
+
|
|
910
|
+
// if (!sectionsEN || !sectionsPT) {
|
|
911
|
+
// throw new Error(`Unknown concept: ${conceptSlug}`);
|
|
912
|
+
// }
|
|
913
|
+
|
|
914
|
+
// // ✅ Step 3: Detect if AI interleaved EN/PT sections
|
|
915
|
+
// const interleavedMatches = cleanedResponse.match(
|
|
916
|
+
// /(EN:\s*[\s\S]+?PT:\s*[\s\S]+?)(?=EN:|$)/
|
|
917
|
+
// );
|
|
918
|
+
// let extractedEN = '';
|
|
919
|
+
// let extractedPT = '';
|
|
920
|
+
|
|
921
|
+
// if (interleavedMatches) {
|
|
922
|
+
// console.warn(
|
|
923
|
+
// '⚠️ Detected interleaved EN/PT sections. Parsing accordingly.'
|
|
924
|
+
// );
|
|
925
|
+
// extractedEN = interleavedMatches
|
|
926
|
+
// .map(
|
|
927
|
+
// (pair) => pair.match(/EN:\s*([\s\S]+?)\s*PT:/)?.[1]?.trim() || ''
|
|
928
|
+
// )
|
|
929
|
+
// .join('\n\n');
|
|
930
|
+
// extractedPT = interleavedMatches
|
|
931
|
+
// .map((pair) => pair.match(/PT:\s*([\s\S]+)/)?.[1]?.trim() || '')
|
|
932
|
+
// .join('\n\n');
|
|
933
|
+
// } else {
|
|
934
|
+
// console.log(
|
|
935
|
+
// '✅ Standard format detected (EN block followed by PT block).'
|
|
936
|
+
// );
|
|
937
|
+
// extractedEN =
|
|
938
|
+
// cleanedResponse.match(/EN:\s*([\s\S]+?)\s*PT:/)?.[1]?.trim() || '';
|
|
939
|
+
// extractedPT =
|
|
940
|
+
// cleanedResponse.match(/PT:\s*([\s\S]+)/)?.[1]?.trim() || '';
|
|
941
|
+
// }
|
|
942
|
+
|
|
943
|
+
// console.log(
|
|
944
|
+
// '✅ [MATCH SUCCESS] Extracted English Content:',
|
|
945
|
+
// extractedEN.slice(0, 500) + '...'
|
|
946
|
+
// );
|
|
947
|
+
// console.log(
|
|
948
|
+
// '✅ [MATCH SUCCESS] Extracted Portuguese Content:',
|
|
949
|
+
// extractedPT.slice(0, 500) + '...'
|
|
950
|
+
// );
|
|
951
|
+
|
|
952
|
+
// // ✅ Step 4: Extract structured sections dynamically
|
|
953
|
+
// function extractSections(text: string, sectionTitles: string[]) {
|
|
954
|
+
// return sectionTitles.map((title) => {
|
|
955
|
+
// console.log(`🔍 [PROCESSING] Extracting section: "${title}"`);
|
|
956
|
+
|
|
957
|
+
// const regex = new RegExp(
|
|
958
|
+
// `\\d+\\.\\s*${title}:\\s*([\\s\\S]+?)(?=\\n\\d+\\.\\s*[A-Z]|$)`
|
|
959
|
+
// );
|
|
960
|
+
// const match = text.match(regex);
|
|
961
|
+
|
|
962
|
+
// if (!match) {
|
|
963
|
+
// throw new Error(`❌ Missing section: "${title}"`);
|
|
964
|
+
// }
|
|
965
|
+
|
|
966
|
+
// const paragraphs = match[1]
|
|
967
|
+
// .trim()
|
|
968
|
+
// .split(/\n{2,}/)
|
|
969
|
+
// .map((p) => p.trim())
|
|
970
|
+
// .filter((p) => p.length > 0);
|
|
971
|
+
|
|
972
|
+
// console.log(
|
|
973
|
+
// `✅ [EXTRACTED] "${title}" - Parsed Content:`,
|
|
974
|
+
// paragraphs
|
|
975
|
+
// );
|
|
976
|
+
// return { type: 'section', title, content: paragraphs };
|
|
977
|
+
// });
|
|
978
|
+
// }
|
|
979
|
+
|
|
980
|
+
// const structuredContentEN = extractSections(extractedEN, sectionsEN);
|
|
981
|
+
// const structuredContentPT = extractSections(extractedPT, sectionsPT);
|
|
982
|
+
|
|
983
|
+
// console.log(
|
|
984
|
+
// '🎯 [FINAL RESULT] Parsed English Content:',
|
|
985
|
+
// JSON.stringify(structuredContentEN, null, 2)
|
|
986
|
+
// );
|
|
987
|
+
// console.log(
|
|
988
|
+
// '🎯 [FINAL RESULT] Parsed Portuguese Content:',
|
|
989
|
+
// JSON.stringify(structuredContentPT, null, 2)
|
|
990
|
+
// );
|
|
991
|
+
|
|
992
|
+
// console.log(
|
|
993
|
+
// '✅ [COMPLETE] Structured content parsing finished successfully.'
|
|
994
|
+
// );
|
|
995
|
+
|
|
996
|
+
// return {
|
|
997
|
+
// structuredContentEN,
|
|
998
|
+
// structuredContentPT,
|
|
999
|
+
// };
|
|
1000
|
+
// } catch (error) {
|
|
1001
|
+
// console.error(`❌ Parsing failed for ${conceptSlug}. Logging failure.`);
|
|
1002
|
+
|
|
1003
|
+
// // ✅ Store failure in KV for debugging
|
|
1004
|
+
// const kvFailuresStore = this.context.kvConceptFailuresStore();
|
|
1005
|
+
// const failureKey = `failures:content:parsing:${conceptSlug}:${new Date().toISOString()}`;
|
|
1006
|
+
|
|
1007
|
+
// await kvFailuresStore.put(
|
|
1008
|
+
// failureKey,
|
|
1009
|
+
// JSON.stringify({
|
|
1010
|
+
// error: (error as Error).message,
|
|
1011
|
+
// conceptSlug,
|
|
1012
|
+
// cleanedResponse,
|
|
1013
|
+
// timestamp: new Date().toISOString(),
|
|
1014
|
+
// })
|
|
1015
|
+
// );
|
|
1016
|
+
|
|
1017
|
+
// console.error(`🚨 Failure logged in KV: ${failureKey}`);
|
|
1018
|
+
// throw error;
|
|
1019
|
+
// }
|
|
1020
|
+
// }
|
|
1021
|
+
|
|
787
1022
|
// private parseStructuredContent(
|
|
788
1023
|
// conceptSlug: Concept,
|
|
789
1024
|
// response: string
|
package/db/schema.ts
CHANGED
|
@@ -181,6 +181,68 @@ export const astroAspects = sqliteTable(
|
|
|
181
181
|
]
|
|
182
182
|
);
|
|
183
183
|
|
|
184
|
+
export const astroReports = sqliteTable(
|
|
185
|
+
"astro_reports",
|
|
186
|
+
{
|
|
187
|
+
id: text("id").primaryKey(), // Unique identifier
|
|
188
|
+
type: text("type").notNull(), // 'planet', 'key_point', 'karmic_point', 'hermetic_part'
|
|
189
|
+
name: text("name").notNull(), // Slug (e.g., 'sun', 'pars_fortuna')
|
|
190
|
+
sign: text("sign"), // Slug (e.g., 'aries', 'taurus') - nullable for house-only reports
|
|
191
|
+
house: integer("house"), // Nullable, only for applicable placements
|
|
192
|
+
enDescription: text("en_description"), // General explanation of the placement
|
|
193
|
+
ptDescription: text("pt_description"),
|
|
194
|
+
enReport: text("en_report"), // Full report content in English
|
|
195
|
+
ptReport: text("pt_report"), // Full report content in Portuguese
|
|
196
|
+
createdAt: integer("created_at").default(sql`CURRENT_TIMESTAMP`),
|
|
197
|
+
updatedAt: integer("updated_at").default(sql`CURRENT_TIMESTAMP`),
|
|
198
|
+
},
|
|
199
|
+
(t) => [
|
|
200
|
+
index("astro_reports_type_idx").on(t.type),
|
|
201
|
+
index("astro_reports_name_sign_idx").on(t.name, t.sign), // Most critical index (name + sign)
|
|
202
|
+
index("astro_reports_name_house_idx").on(t.name, t.house), // Most critical index (name + house)
|
|
203
|
+
]
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
export const houseReports = sqliteTable(
|
|
207
|
+
"house_reports",
|
|
208
|
+
{
|
|
209
|
+
id: text("id").primaryKey(), // Unique identifier
|
|
210
|
+
sign: text("sign").notNull(), // Lowercase slug (e.g., 'aries', 'taurus')
|
|
211
|
+
house: integer("house").notNull(), // House placement (1-12)
|
|
212
|
+
enDescription: text("en_description"), // General explanation of the sign in a house
|
|
213
|
+
ptDescription: text("pt_description"),
|
|
214
|
+
enReport: text("en_report"), // Full report content in English
|
|
215
|
+
ptReport: text("pt_report"), // Full report content in Portuguese
|
|
216
|
+
createdAt: integer("created_at").default(sql`CURRENT_TIMESTAMP`),
|
|
217
|
+
updatedAt: integer("updated_at").default(sql`CURRENT_TIMESTAMP`),
|
|
218
|
+
},
|
|
219
|
+
(t) => [
|
|
220
|
+
index("house_reports_sign_house_idx").on(t.sign, t.house), // Optimized index for sign + house lookups
|
|
221
|
+
]
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
export const aspectReports = sqliteTable(
|
|
225
|
+
"aspect_reports",
|
|
226
|
+
{
|
|
227
|
+
id: text("id").primaryKey(), // Unique identifier
|
|
228
|
+
aspectingPlanet: text("aspecting_planet").notNull(), // Planet/point initiating the aspect (e.g., 'sun', 'mars')
|
|
229
|
+
aspectedPlanet: text("aspected_planet").notNull(), // Planet/point receiving the aspect (e.g., 'moon', 'venus')
|
|
230
|
+
aspect: text("aspect").notNull(), // Aspect type ('conjunction', 'trine', 'square', etc.)
|
|
231
|
+
enDescription: text("en_description"), // General explanation of the aspect
|
|
232
|
+
ptDescription: text("pt_description"),
|
|
233
|
+
enReport: text("en_report"), // Full report content in English
|
|
234
|
+
ptReport: text("pt_report"), // Full report content in Portuguese
|
|
235
|
+
createdAt: integer("created_at").default(sql`CURRENT_TIMESTAMP`),
|
|
236
|
+
updatedAt: integer("updated_at").default(sql`CURRENT_TIMESTAMP`),
|
|
237
|
+
},
|
|
238
|
+
(t) => [
|
|
239
|
+
index("aspect_reports_aspecting_idx").on(t.aspectingPlanet),
|
|
240
|
+
index("aspect_reports_aspected_idx").on(t.aspectedPlanet),
|
|
241
|
+
index("aspect_reports_aspect_idx").on(t.aspect),
|
|
242
|
+
index("aspect_reports_combined_idx").on(t.aspectingPlanet, t.aspectedPlanet, t.aspect), // Optimized for aspect lookups
|
|
243
|
+
]
|
|
244
|
+
);
|
|
245
|
+
|
|
184
246
|
export const concepts = sqliteTable(
|
|
185
247
|
'concepts',
|
|
186
248
|
{
|
package/package.json
CHANGED
package/types/scopes/legacy.ts
CHANGED
|
@@ -1,21 +1,109 @@
|
|
|
1
|
+
import { pointNameMap } from '../../utils/astroPrompts/pointNameMap';
|
|
1
2
|
import { BackendBindings } from './cloudflare';
|
|
2
3
|
import { ControlNetConfig, Gender } from './generic';
|
|
3
4
|
|
|
4
5
|
export const zodiacSigns = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
"aries",
|
|
7
|
+
"taurus",
|
|
8
|
+
"gemini",
|
|
9
|
+
"cancer",
|
|
10
|
+
"leo",
|
|
11
|
+
"virgo",
|
|
12
|
+
"libra",
|
|
13
|
+
"scorpio",
|
|
14
|
+
"sagittarius",
|
|
15
|
+
"capricorn",
|
|
16
|
+
"aquarius",
|
|
17
|
+
"pisces",
|
|
18
|
+
] as const;
|
|
19
|
+
export type ZodiacSignSlug = (typeof zodiacSigns)[number];
|
|
20
|
+
|
|
21
|
+
// Houses
|
|
22
|
+
export const houses = Array.from({ length: 12 }, (_, i) => i + 1);
|
|
23
|
+
|
|
24
|
+
// Aspects
|
|
25
|
+
const aspects = ["conjunction", "opposition", "trine", "square", "sextile"] as const;
|
|
26
|
+
export type AspectType = (typeof aspects)[number];
|
|
27
|
+
|
|
28
|
+
export const pointTypes: Record<PointSlug, string> = {
|
|
29
|
+
sun: "planet",
|
|
30
|
+
moon: "planet",
|
|
31
|
+
mercury: "planet",
|
|
32
|
+
venus: "planet",
|
|
33
|
+
mars: "planet",
|
|
34
|
+
jupiter: "planet",
|
|
35
|
+
saturn: "planet",
|
|
36
|
+
uranus: "planet",
|
|
37
|
+
neptune: "planet",
|
|
38
|
+
pluto: "planet",
|
|
39
|
+
ascendant: "key_point",
|
|
40
|
+
descendant: "key_point",
|
|
41
|
+
midheaven: "key_point",
|
|
42
|
+
imum_coeli: "key_point",
|
|
43
|
+
north_node: "karmic_point",
|
|
44
|
+
south_node: "karmic_point",
|
|
45
|
+
chiron: "karmic_point",
|
|
46
|
+
lilith: "hermetic_part",
|
|
47
|
+
vertex: "hermetic_part",
|
|
48
|
+
antivertex: "hermetic_part",
|
|
49
|
+
pars_fortuna: "hermetic_part",
|
|
50
|
+
pars_spiritus: "hermetic_part",
|
|
51
|
+
pars_victoria: "hermetic_part",
|
|
52
|
+
pars_amoris: "hermetic_part",
|
|
53
|
+
pars_eros: "hermetic_part",
|
|
54
|
+
pars_fortitudo: "hermetic_part",
|
|
55
|
+
pars_necessitatis: "hermetic_part",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Subset of 14 points for aspects
|
|
59
|
+
export const aspectPoints: PointSlug[] = [
|
|
60
|
+
"sun",
|
|
61
|
+
"moon",
|
|
62
|
+
"mercury",
|
|
63
|
+
"venus",
|
|
64
|
+
"mars",
|
|
65
|
+
"jupiter",
|
|
66
|
+
"saturn",
|
|
67
|
+
"uranus",
|
|
68
|
+
"neptune",
|
|
69
|
+
"pluto",
|
|
70
|
+
"ascendant",
|
|
71
|
+
"descendant",
|
|
72
|
+
"midheaven",
|
|
73
|
+
"imum_coeli",
|
|
17
74
|
];
|
|
18
75
|
|
|
76
|
+
export type PointSlug = keyof typeof pointNameMap;
|
|
77
|
+
|
|
78
|
+
export type ZodiacSign =
|
|
79
|
+
| 'aries'
|
|
80
|
+
| 'taurus'
|
|
81
|
+
| 'gemini'
|
|
82
|
+
| 'cancer'
|
|
83
|
+
| 'leo'
|
|
84
|
+
| 'virgo'
|
|
85
|
+
| 'libra'
|
|
86
|
+
| 'scorpio'
|
|
87
|
+
| 'sagittarius'
|
|
88
|
+
| 'capricorn'
|
|
89
|
+
| 'aquarius'
|
|
90
|
+
| 'pisces';
|
|
91
|
+
|
|
92
|
+
export const zodiacSignMap: Record<ZodiacSign, string> = {
|
|
93
|
+
aries: 'Aries',
|
|
94
|
+
taurus: 'Taurus',
|
|
95
|
+
gemini: 'Gemini',
|
|
96
|
+
cancer: 'Cancer',
|
|
97
|
+
leo: 'Leo',
|
|
98
|
+
virgo: 'Virgo',
|
|
99
|
+
libra: 'Libra',
|
|
100
|
+
scorpio: 'Scorpio',
|
|
101
|
+
sagittarius: 'Sagittarius',
|
|
102
|
+
capricorn: 'Capricorn',
|
|
103
|
+
aquarius: 'Aquarius',
|
|
104
|
+
pisces: 'Pisces',
|
|
105
|
+
};
|
|
106
|
+
|
|
19
107
|
export interface ChatGPTOptions {
|
|
20
108
|
model?: string;
|
|
21
109
|
options?: Record<string, any>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { PointSlug, ZodiacSign, zodiacSignMap } from '../../types/scopes/legacy';
|
|
2
|
+
import { pointNameMap } from './pointNameMap';
|
|
3
|
+
|
|
4
|
+
// Interfaces for each function's parameters
|
|
5
|
+
interface PlanetOrPointParams {
|
|
6
|
+
type: 'planet' | 'key_point' | 'karmic_point' | 'hermetic_part';
|
|
7
|
+
sign: ZodiacSign;
|
|
8
|
+
pointName: PointSlug;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface HouseParams {
|
|
12
|
+
houseNumber: number; // e.g., "1", "2", etc.
|
|
13
|
+
sign: ZodiacSign;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface AspectParams {
|
|
17
|
+
aspectingPlanet: PointSlug;
|
|
18
|
+
aspectedPlanet: PointSlug;
|
|
19
|
+
aspectingType: string; // e.g., "conjunct", "square", "trine"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Modular astroPrompts object
|
|
23
|
+
export const astroPrompts = {
|
|
24
|
+
planetOrPoint: ({ type, sign, pointName }: PlanetOrPointParams): string => {
|
|
25
|
+
const validTypes = ['planet', 'key_point', 'karmic_point', 'hermetic_part'];
|
|
26
|
+
if (!validTypes.includes(type)) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Invalid type for planetOrPoint: ${type}. Must be one of ${validTypes.join(
|
|
29
|
+
', '
|
|
30
|
+
)}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const formattedPointName = pointNameMap[pointName] || pointName;
|
|
35
|
+
return `
|
|
36
|
+
Make me a report for the astrological placement: ${formattedPointName} in ${zodiacSignMap[sign]}.
|
|
37
|
+
The response should include an English version and a Portuguese version.
|
|
38
|
+
Use "###" for titles and "####" for subtitles.
|
|
39
|
+
The response format should be exactly like this:
|
|
40
|
+
|
|
41
|
+
-- EN
|
|
42
|
+
|
|
43
|
+
[English Report - structured with headings, sections, and detailed explanations]
|
|
44
|
+
|
|
45
|
+
-- PT
|
|
46
|
+
|
|
47
|
+
[Portuguese Report - structured exactly the same as the English version]
|
|
48
|
+
`.trim();
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
house: ({ houseNumber, sign }: HouseParams): string => {
|
|
52
|
+
return `
|
|
53
|
+
Make me a report for the astrological placement: House ${houseNumber} in ${zodiacSignMap[sign]}.
|
|
54
|
+
The response should include an English version and a Portuguese version.
|
|
55
|
+
Use "###" for titles and "####" for subtitles.
|
|
56
|
+
The response format should be exactly like this:
|
|
57
|
+
|
|
58
|
+
-- EN
|
|
59
|
+
|
|
60
|
+
[English Report - structured with headings, sections, and detailed explanations]
|
|
61
|
+
|
|
62
|
+
-- PT
|
|
63
|
+
|
|
64
|
+
[Portuguese Report - structured exactly the same as the English version]
|
|
65
|
+
`.trim();
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
aspect: ({
|
|
69
|
+
aspectingPlanet,
|
|
70
|
+
aspectedPlanet,
|
|
71
|
+
aspectingType,
|
|
72
|
+
}: AspectParams): string => {
|
|
73
|
+
return `
|
|
74
|
+
Make me a report for the astrological aspect: ${
|
|
75
|
+
pointNameMap[aspectingPlanet] || aspectingPlanet
|
|
76
|
+
} ${aspectingType} ${pointNameMap[aspectedPlanet] || aspectedPlanet}.
|
|
77
|
+
The response should include an English version and a Portuguese version.
|
|
78
|
+
Use "###" for titles and "####" for subtitles.
|
|
79
|
+
The response format should be exactly like this:
|
|
80
|
+
|
|
81
|
+
-- EN
|
|
82
|
+
|
|
83
|
+
[English Report - structured with headings, sections, and detailed explanations]
|
|
84
|
+
|
|
85
|
+
-- PT
|
|
86
|
+
|
|
87
|
+
[Portuguese Report - structured exactly the same as the English version]
|
|
88
|
+
`.trim();
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const pointNameMap: Record<string, string> = {
|
|
2
|
+
sun: "Sun",
|
|
3
|
+
moon: "Moon",
|
|
4
|
+
mercury: "Mercury",
|
|
5
|
+
venus: "Venus",
|
|
6
|
+
mars: "Mars",
|
|
7
|
+
jupiter: "Jupiter",
|
|
8
|
+
saturn: "Saturn",
|
|
9
|
+
uranus: "Uranus",
|
|
10
|
+
neptune: "Neptune",
|
|
11
|
+
pluto: "Pluto",
|
|
12
|
+
ascendant: "Ascendant",
|
|
13
|
+
descendant: "Descendant",
|
|
14
|
+
midheaven: "Midheaven",
|
|
15
|
+
imum_coeli: "Imum Coeli",
|
|
16
|
+
north_node: "North Node",
|
|
17
|
+
south_node: "South Node",
|
|
18
|
+
chiron: "Chiron",
|
|
19
|
+
lilith: "Lilith",
|
|
20
|
+
vertex: "Vertex",
|
|
21
|
+
antivertex: "Antivertex",
|
|
22
|
+
pars_fortuna: "Pars Fortuna",
|
|
23
|
+
pars_spiritus: "Pars Spiritus",
|
|
24
|
+
pars_victoria: "Pars Victoria",
|
|
25
|
+
pars_amoris: "Pars Amoris",
|
|
26
|
+
pars_eros: "Pars Eros",
|
|
27
|
+
pars_fortitudo: "Pars Fortitudo",
|
|
28
|
+
pars_necessitatis: "Pars Necessitatis",
|
|
29
|
+
};
|
package/utils/index.ts
CHANGED
|
@@ -101,7 +101,9 @@ export const verifyToken = async (
|
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
-
export const providers: (c: AuthCtx) => Record<Provider, ProviderInfo> = (
|
|
104
|
+
export const providers: (c: AuthCtx) => Record<Provider, ProviderInfo> = (
|
|
105
|
+
c
|
|
106
|
+
) => ({
|
|
105
107
|
google: {
|
|
106
108
|
serverUrl: 'https://accounts.google.com/.well-known/openid-configuration', // ✅ Correct Discovery URL
|
|
107
109
|
clientId: c.env.GOOGLE_OAUTH_CLIENT_ID!,
|
|
@@ -120,3 +122,10 @@ export function normalizeName(name: string): string {
|
|
|
120
122
|
return name.toLowerCase().replace(/\s+/g, '-');
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
export function chunkArray<T>(array: T[], size: number): T[][] {
|
|
126
|
+
const result = [];
|
|
127
|
+
for (let i = 0; i < array.length; i += size) {
|
|
128
|
+
result.push(array.slice(i, i + size));
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|