@pasosdejesus/mpdj 0.1.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.
Files changed (103) hide show
  1. package/LICENSE.md +16 -0
  2. package/SKILL.md +310 -0
  3. package/dist/SKILL.md +310 -0
  4. package/dist/abis/PasosDeJesusCredentials.json +1342 -0
  5. package/dist/blockchain/AddSlearnButton.d.ts +5 -0
  6. package/dist/blockchain/AddSlearnButton.js +58 -0
  7. package/dist/blockchain/SlearnInfo.d.ts +22 -0
  8. package/dist/blockchain/SlearnInfo.js +68 -0
  9. package/dist/blockchain/admin-credentials.d.ts +29 -0
  10. package/dist/blockchain/admin-credentials.js +91 -0
  11. package/dist/blockchain/credential-cache.d.ts +33 -0
  12. package/dist/blockchain/credential-cache.js +129 -0
  13. package/dist/blockchain/credential-images.d.ts +30 -0
  14. package/dist/blockchain/credential-images.js +199 -0
  15. package/dist/blockchain/credentials.d.ts +66 -0
  16. package/dist/blockchain/credentials.js +128 -0
  17. package/dist/blockchain/ecosystem-addresses.d.ts +9 -0
  18. package/dist/blockchain/ecosystem-addresses.js +23 -0
  19. package/dist/blockchain/index.d.ts +6 -0
  20. package/dist/blockchain/index.js +12 -0
  21. package/dist/ia/adJ.md +181 -0
  22. package/dist/ia/alineacion_bayes_general.md +60 -0
  23. package/dist/ia/alineacion_bayes_profecia_mayor20G.md +220 -0
  24. package/dist/ia/alineacion_servicio.md +49 -0
  25. package/dist/ia/alineacion_testimonios.md +268 -0
  26. package/dist/ia/construir_en_adJ_y_celo.md +239 -0
  27. package/dist/ia/entrenamiento-marcos.jsonl +23 -0
  28. package/dist/ia/entrenamiento-no-adivinar.jsonl +15 -0
  29. package/dist/ia/estilo_entrenamiento.jsonl +10 -0
  30. package/dist/ia/indice_entrenamiento.json +43 -0
  31. package/dist/ia/m.jsonl +3 -0
  32. package/dist/ia/manifiesto.md +45 -0
  33. package/dist/ia/piedra_fundamental/README.md +31 -0
  34. package/dist/ia/piedra_fundamental/marcos-01.usfm +68 -0
  35. package/dist/ia/piedra_fundamental/marcos-02.usfm +46 -0
  36. package/dist/ia/piedra_fundamental/marcos-03.usfm +48 -0
  37. package/dist/ia/piedra_fundamental/marcos-04.usfm +59 -0
  38. package/dist/ia/piedra_fundamental/marcos-05.usfm +65 -0
  39. package/dist/ia/piedra_fundamental/marcos-06.usfm +78 -0
  40. package/dist/ia/piedra_fundamental/marcos-07.usfm +53 -0
  41. package/dist/ia/piedra_fundamental/marcos-08.usfm +63 -0
  42. package/dist/ia/piedra_fundamental/marcos-09.usfm +82 -0
  43. package/dist/ia/piedra_fundamental/marcos-10.usfm +88 -0
  44. package/dist/ia/piedra_fundamental/marcos-11.usfm +52 -0
  45. package/dist/ia/piedra_fundamental/marcos-12.usfm +62 -0
  46. package/dist/ia/piedra_fundamental/marcos-13.usfm +48 -0
  47. package/dist/ia/piedra_fundamental/marcos-14.usfm +108 -0
  48. package/dist/ia/piedra_fundamental/marcos-15.usfm +70 -0
  49. package/dist/ia/piedra_fundamental/marcos-16.usfm +32 -0
  50. package/dist/ia/principles.md +35 -0
  51. package/dist/ia/referencia-marcos.md +152 -0
  52. package/dist/ia/verificacion_cristologica.json +128 -0
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.js +159 -0
  55. package/dist/tasks/adJ.d.ts +1 -0
  56. package/dist/tasks/adJ.js +37 -0
  57. package/dist/tasks/ai-verify-core.d.ts +27 -0
  58. package/dist/tasks/ai-verify-core.js +82 -0
  59. package/dist/tasks/ai.d.ts +3 -0
  60. package/dist/tasks/ai.js +96 -0
  61. package/dist/tasks/credentials.d.ts +4 -0
  62. package/dist/tasks/credentials.js +627 -0
  63. package/dist/tasks/principles.d.ts +1 -0
  64. package/dist/tasks/principles.js +37 -0
  65. package/dist/tasks/req.d.ts +9 -0
  66. package/dist/tasks/req.js +1164 -0
  67. package/dist/tasks/skill.d.ts +1 -0
  68. package/dist/tasks/skill.js +34 -0
  69. package/dist/test-utils/blockchain-mocks.d.ts +130 -0
  70. package/dist/test-utils/blockchain-mocks.js +112 -0
  71. package/ia/adJ.md +181 -0
  72. package/ia/alineacion_bayes_general.md +60 -0
  73. package/ia/alineacion_bayes_profecia_mayor20G.md +220 -0
  74. package/ia/alineacion_servicio.md +49 -0
  75. package/ia/alineacion_testimonios.md +268 -0
  76. package/ia/construir_en_adJ_y_celo.md +239 -0
  77. package/ia/entrenamiento-marcos.jsonl +23 -0
  78. package/ia/entrenamiento-no-adivinar.jsonl +15 -0
  79. package/ia/estilo_entrenamiento.jsonl +10 -0
  80. package/ia/indice_entrenamiento.json +43 -0
  81. package/ia/m.jsonl +3 -0
  82. package/ia/manifiesto.md +45 -0
  83. package/ia/piedra_fundamental/README.md +31 -0
  84. package/ia/piedra_fundamental/marcos-01.usfm +68 -0
  85. package/ia/piedra_fundamental/marcos-02.usfm +46 -0
  86. package/ia/piedra_fundamental/marcos-03.usfm +48 -0
  87. package/ia/piedra_fundamental/marcos-04.usfm +59 -0
  88. package/ia/piedra_fundamental/marcos-05.usfm +65 -0
  89. package/ia/piedra_fundamental/marcos-06.usfm +78 -0
  90. package/ia/piedra_fundamental/marcos-07.usfm +53 -0
  91. package/ia/piedra_fundamental/marcos-08.usfm +63 -0
  92. package/ia/piedra_fundamental/marcos-09.usfm +82 -0
  93. package/ia/piedra_fundamental/marcos-10.usfm +88 -0
  94. package/ia/piedra_fundamental/marcos-11.usfm +52 -0
  95. package/ia/piedra_fundamental/marcos-12.usfm +62 -0
  96. package/ia/piedra_fundamental/marcos-13.usfm +48 -0
  97. package/ia/piedra_fundamental/marcos-14.usfm +108 -0
  98. package/ia/piedra_fundamental/marcos-15.usfm +70 -0
  99. package/ia/piedra_fundamental/marcos-16.usfm +32 -0
  100. package/ia/principles.md +35 -0
  101. package/ia/referencia-marcos.md +152 -0
  102. package/ia/verificacion_cristologica.json +128 -0
  103. package/package.json +73 -0
@@ -0,0 +1,5 @@
1
+ interface AddSlearnButtonProps {
2
+ lang: string;
3
+ }
4
+ export declare function AddSlearnButton({ lang }: AddSlearnButtonProps): import("react").JSX.Element;
5
+ export {};
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect, useMemo } from 'react';
4
+ import { Button } from '@pasosdejesus/m/shadcn-components/ui/button';
5
+ import { PlusCircle, CheckCircle, AlertCircle } from 'lucide-react';
6
+ import { SLEARN_ADDRESSES, resolveNetwork } from './ecosystem-addresses.js';
7
+ // Fallback addresses for when env is not available
8
+ const FALLBACK_SLEARN = {
9
+ celo: '0x27fd41Bea85C39254f2B12789eB37a1543152CC1',
10
+ celoSepolia: '0x9fBa3A2Ca0275c4D7A3eA341923f8c531e913BFA',
11
+ };
12
+ export function AddSlearnButton({ lang }) {
13
+ const [state, setState] = useState('idle');
14
+ const [isMiniPay, setIsMiniPay] = useState(false);
15
+ const hasWallet = typeof window !== 'undefined' && !!window.ethereum;
16
+ const slearnAddr = useMemo(() => {
17
+ const network = resolveNetwork();
18
+ return SLEARN_ADDRESSES[network] || FALLBACK_SLEARN[network] || FALLBACK_SLEARN.celo;
19
+ }, []);
20
+ const shortAddr = slearnAddr?.slice(0, 6) + '…' + slearnAddr?.slice(-4);
21
+ useEffect(() => {
22
+ if (typeof window !== 'undefined' && window.ethereum?.isMiniPay) {
23
+ setIsMiniPay(true);
24
+ }
25
+ }, []);
26
+ const addToken = async () => {
27
+ if (!hasWallet)
28
+ return;
29
+ setState('loading');
30
+ try {
31
+ const wasAdded = await window.ethereum?.request({
32
+ method: 'wallet_watchAsset',
33
+ params: {
34
+ type: 'ERC20',
35
+ options: {
36
+ address: slearnAddr,
37
+ symbol: 'SLEARN',
38
+ decimals: 2,
39
+ image: 'https://learn.tg/img/slearn-icon.svg',
40
+ },
41
+ },
42
+ });
43
+ setState(wasAdded ? 'success' : 'error');
44
+ }
45
+ catch {
46
+ setState('error');
47
+ }
48
+ };
49
+ return (_jsx("div", { className: "text-center", children: isMiniPay ? (_jsx("span", { className: "text-xs text-gray-400", children: lang === 'es'
50
+ ? `SLEARN: ${slearnAddr} — 2 decimales`
51
+ : `SLEARN: ${slearnAddr} — 2 decimals` })) : state === 'success' ? (_jsxs("span", { className: "text-xs text-emerald-600 flex items-center justify-center gap-1", children: [_jsx(CheckCircle, { className: "h-3 w-3" }), lang === 'es' ? '¡SLEARN agregado a tu billetera!' : 'SLEARN added to your wallet!'] })) : state === 'error' ? (_jsxs("span", { className: "text-xs text-red-500 flex items-center justify-center gap-1", children: [_jsx(AlertCircle, { className: "h-3 w-3" }), lang === 'es'
52
+ ? `Agrega manualmente: ${shortAddr}, 2 decimales`
53
+ : `Add manually: ${shortAddr}, 2 decimals`] })) : (_jsxs(Button, { variant: "outline", size: "sm", onClick: addToken, disabled: !hasWallet || state === 'loading', className: "text-xs gap-1", children: [_jsx(PlusCircle, { className: "h-3 w-3" }), state === 'loading'
54
+ ? (lang === 'es' ? 'Agregando…' : 'Adding…')
55
+ : lang === 'es'
56
+ ? 'Agregar SLEARN a mi billetera'
57
+ : 'Add SLEARN to my wallet'] })) }));
58
+ }
@@ -0,0 +1,22 @@
1
+ interface StepConfig {
2
+ title: string;
3
+ desc: string;
4
+ icon?: 'donate' | 'earn' | 'course' | 'redeem';
5
+ }
6
+ interface LinkConfig {
7
+ label: string;
8
+ href: string;
9
+ external?: boolean;
10
+ }
11
+ export interface SlearnInfoProps {
12
+ locale?: string;
13
+ isVerified?: boolean;
14
+ /** Description shown below the title (overrides built-in description) */
15
+ description?: string;
16
+ /** Custom steps. Falls back to DEFAULT_STEPS[locale] if not provided */
17
+ steps?: StepConfig[];
18
+ /** External links shown at the bottom. Falls back to DEFAULT_LINKS. Pass [] to hide. */
19
+ links?: LinkConfig[];
20
+ }
21
+ export declare function SlearnInfo({ locale, isVerified, description, steps, links }: SlearnInfoProps): import("react").JSX.Element;
22
+ export {};
@@ -0,0 +1,68 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Card, CardContent } from '@pasosdejesus/m/shadcn-components/ui/card';
5
+ import { Info, GraduationCap, BadgeCheck, ArrowRightLeft, ExternalLink } from 'lucide-react';
6
+ import { SLEARN_ADDRESSES, resolveNetwork } from './ecosystem-addresses.js';
7
+ const DEFAULT_STEPS = {
8
+ en: [
9
+ { title: 'Donate', desc: 'You donate to a region in sivel.xyz', icon: 'donate' },
10
+ { title: 'Earn SLEARN', desc: '10% of your donation is minted as SLEARN to your wallet', icon: 'earn' },
11
+ { title: 'Take courses', desc: 'Use SLEARN to pay for courses on learn.tg', icon: 'course' },
12
+ { title: 'Redeem', desc: 'Complete a premium course → get SBT → redeem SLEARN on stable-sl.pdJ.app for Leones (Sierra Leone) or USDT (worldwide)', icon: 'redeem' },
13
+ ],
14
+ es: [
15
+ { title: 'Donas', desc: 'Donas a una región en sivel.xyz', icon: 'donate' },
16
+ { title: 'Ganas SLEARN', desc: 'El 10% de tu donación se mintea como SLEARN a tu billetera', icon: 'earn' },
17
+ { title: 'Tomas cursos', desc: 'Usa SLEARN para pagar cursos en learn.tg', icon: 'course' },
18
+ { title: 'Canjeas', desc: 'Completa un curso premium → obtén SBT → canjea SLEARN en stable-sl.pdJ.app por Leones (Sierra Leona) o USDT (todo el mundo)', icon: 'redeem' },
19
+ ],
20
+ };
21
+ const DEFAULT_LINKS = [
22
+ { label: 'learn.tg', href: 'https://learn.tg' },
23
+ { label: 'stable-sl', href: 'https://stable-sl.pdJ.app' },
24
+ ];
25
+ const tS = {
26
+ en: {
27
+ title: '🎓 SLEARN Cashback',
28
+ what: 'What is SLEARN?',
29
+ description: 'When you donate, you earn 10% back in SLEARN tokens — a reward for your generosity.',
30
+ contractLink: 'Verified contract',
31
+ unverified: '🔐 Verify on learn.tg to unlock SLEARN and start taking courses.',
32
+ learnTg: 'Take courses on learn.tg',
33
+ stableSl: 'Redeem on stable-sl',
34
+ },
35
+ es: {
36
+ title: '🎓 SLEARN Cashback',
37
+ what: '¿Qué es SLEARN?',
38
+ description: 'Al donar, recibes el 10% de vuelta en tokens SLEARN — una recompensa por tu generosidad.',
39
+ contractLink: 'Contrato verificado',
40
+ unverified: '🔐 Verifícate en learn.tg para desbloquear SLEARN y comenzar a tomar cursos.',
41
+ learnTg: 'Tomar cursos en learn.tg',
42
+ stableSl: 'Canjear en stable-sl',
43
+ },
44
+ };
45
+ const stepIcons = {
46
+ donate: ArrowRightLeft,
47
+ earn: GraduationCap,
48
+ course: GraduationCap,
49
+ redeem: BadgeCheck,
50
+ };
51
+ export function SlearnInfo({ locale = 'en', isVerified, description, steps, links }) {
52
+ const t = (k) => (tS[locale]?.[k] || tS.en[k]);
53
+ const [open, setOpen] = useState(false);
54
+ const network = resolveNetwork();
55
+ const slearnAddr = SLEARN_ADDRESSES[network];
56
+ const blockscoutBase = network === 'celo'
57
+ ? 'https://celo.blockscout.com'
58
+ : 'https://celo-sepolia.blockscout.com';
59
+ const finalSteps = steps || DEFAULT_STEPS[locale] || DEFAULT_STEPS.en;
60
+ const finalLinks = links !== undefined ? links : [
61
+ { label: t('learnTg'), href: 'https://learn.tg' },
62
+ { label: t('stableSl'), href: 'https://stable-sl.pdJ.app' },
63
+ ];
64
+ return (_jsxs(Card, { children: [_jsxs("button", { onClick: () => setOpen(!open), className: "w-full flex items-center justify-between p-3 text-left hover:bg-gray-50 transition-colors rounded-t-lg", children: [_jsxs("span", { className: "font-medium flex items-center gap-2", children: [_jsx(GraduationCap, { className: "h-4 w-4 text-purple-600" }), t('what')] }), _jsx(Info, { className: "h-4 w-4 text-gray-400" })] }), open && (_jsxs(CardContent, { className: "pt-0 pb-4 space-y-3 text-sm", children: [_jsx("p", { className: "text-gray-600", children: description || t('description') }), !isVerified && (_jsx("div", { className: "bg-blue-50 border border-blue-200 rounded p-2 text-xs text-blue-800", children: t('unverified') })), _jsx("div", { className: "space-y-2", children: finalSteps.map((step, i) => {
65
+ const Icon = step.icon ? stepIcons[step.icon] : null;
66
+ return (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "flex-shrink-0 w-5 h-5 rounded-full bg-purple-100 text-purple-700 text-xs flex items-center justify-center font-bold", children: i + 1 }), _jsxs("div", { children: [_jsx("strong", { children: step.title }), _jsxs("span", { className: "text-gray-500", children: [" \u2014 ", step.desc] })] }), Icon && _jsx(Icon, { className: "h-3 w-3 text-purple-400 flex-shrink-0 mt-0.5" })] }, i));
67
+ }) }), _jsxs("div", { className: "flex flex-wrap gap-2 pt-1", children: [finalLinks.map((link, i) => (_jsxs("a", { href: link.href, target: link.external !== false ? '_blank' : undefined, rel: link.external !== false ? 'noopener noreferrer' : undefined, className: "text-xs text-purple-600 underline hover:text-purple-800", children: [link.label, " \u2192"] }, i))), slearnAddr && (_jsxs("a", { href: `${blockscoutBase}/address/${slearnAddr}`, target: "_blank", rel: "noopener noreferrer", className: "text-xs text-gray-400 underline hover:text-gray-600 flex items-center gap-1", children: [_jsx(ExternalLink, { className: "h-3 w-3" }), t('contractLink')] }))] })] }))] }));
68
+ }
@@ -0,0 +1,29 @@
1
+ import type { WalletClient } from 'viem';
2
+ /**
3
+ * Grant MINTER_ROLE to a wallet.
4
+ */
5
+ export declare function grantMinter(walletClient: WalletClient, contractAddress: `0x${string}`, address: `0x${string}`): Promise<string>;
6
+ /**
7
+ * Revoke MINTER_ROLE from a wallet.
8
+ */
9
+ export declare function revokeMinter(walletClient: WalletClient, contractAddress: `0x${string}`, address: `0x${string}`): Promise<string>;
10
+ /**
11
+ * Register a new credential type. MINTER_ROLE (backends can register).
12
+ *
13
+ * @param customURI Optional per‑token URI (e.g., ipfs://...). Pass '' for site default.
14
+ * @param maxSupply_ Max supply. 0 = unlimited. Ignored for NFTs with customURI.
15
+ */
16
+ export declare function registerCredentialType(walletClient: WalletClient, contractAddress: `0x${string}`, siteName: string, typeName: string, displayName: string, soulbound: boolean, courseId: number, premium: boolean, customURI?: string, maxSupply_?: number): Promise<number>;
17
+ /**
18
+ * Set max supply for a token. Only DEFAULT_ADMIN_ROLE.
19
+ * Cannot be used on NFTs (their maxSupply is locked at registration).
20
+ */
21
+ export declare function setMaxSupply(walletClient: WalletClient, contractAddress: `0x${string}`, tokenId: number, max: number): Promise<string>;
22
+ /**
23
+ * Set base URI for a site. Only DEFAULT_ADMIN_ROLE.
24
+ */
25
+ export declare function setSiteBaseURI(walletClient: WalletClient, contractAddress: `0x${string}`, siteName: string, uri: string): Promise<string>;
26
+ /**
27
+ * Set or correct a custom token URI. Only DEFAULT_ADMIN_ROLE.
28
+ */
29
+ export declare function setCustomTokenURI(walletClient: WalletClient, contractAddress: `0x${string}`, tokenId: number, customURI: string): Promise<string>;
@@ -0,0 +1,91 @@
1
+ // src/blockchain/admin-credentials.ts
2
+ // Admin functions for PasosDeJesusCredentials.
3
+ // DEFAULT_ADMIN_ROLE required: grantMinter, revokeMinter, setMaxSupply, setSiteBaseURI, setCustomTokenURI.
4
+ // MINTER_ROLE can also call registerCredentialType.
5
+ import pasosDeJesusCredentialsAbi from '../abis/PasosDeJesusCredentials.json' with { type: 'json' };
6
+ const MINTER_ROLE = '0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6';
7
+ /**
8
+ * Grant MINTER_ROLE to a wallet.
9
+ */
10
+ export async function grantMinter(walletClient, contractAddress, address) {
11
+ return await walletClient.writeContract({
12
+ address: contractAddress,
13
+ abi: pasosDeJesusCredentialsAbi,
14
+ functionName: 'grantRole',
15
+ args: [MINTER_ROLE, address],
16
+ chain: walletClient.chain,
17
+ account: walletClient.account,
18
+ });
19
+ }
20
+ /**
21
+ * Revoke MINTER_ROLE from a wallet.
22
+ */
23
+ export async function revokeMinter(walletClient, contractAddress, address) {
24
+ return await walletClient.writeContract({
25
+ address: contractAddress,
26
+ abi: pasosDeJesusCredentialsAbi,
27
+ functionName: 'revokeRole',
28
+ args: [MINTER_ROLE, address],
29
+ chain: walletClient.chain,
30
+ account: walletClient.account,
31
+ });
32
+ }
33
+ /**
34
+ * Register a new credential type. MINTER_ROLE (backends can register).
35
+ *
36
+ * @param customURI Optional per‑token URI (e.g., ipfs://...). Pass '' for site default.
37
+ * @param maxSupply_ Max supply. 0 = unlimited. Ignored for NFTs with customURI.
38
+ */
39
+ export async function registerCredentialType(walletClient, contractAddress, siteName, typeName, displayName, soulbound, courseId, premium, customURI = '', maxSupply_ = 0) {
40
+ const hash = await walletClient.writeContract({
41
+ address: contractAddress,
42
+ abi: pasosDeJesusCredentialsAbi,
43
+ functionName: 'registerCredentialType',
44
+ args: [siteName, typeName, displayName, soulbound, BigInt(courseId), premium, customURI, BigInt(maxSupply_)],
45
+ chain: walletClient.chain,
46
+ account: walletClient.account,
47
+ });
48
+ // TokenId is sequential. The caller should use events to get the exact tokenId.
49
+ // This function returns 0 as placeholder — caller resolves via receipt.
50
+ return 0;
51
+ }
52
+ /**
53
+ * Set max supply for a token. Only DEFAULT_ADMIN_ROLE.
54
+ * Cannot be used on NFTs (their maxSupply is locked at registration).
55
+ */
56
+ export async function setMaxSupply(walletClient, contractAddress, tokenId, max) {
57
+ return await walletClient.writeContract({
58
+ address: contractAddress,
59
+ abi: pasosDeJesusCredentialsAbi,
60
+ functionName: 'setMaxSupply',
61
+ args: [BigInt(tokenId), BigInt(max)],
62
+ chain: walletClient.chain,
63
+ account: walletClient.account,
64
+ });
65
+ }
66
+ /**
67
+ * Set base URI for a site. Only DEFAULT_ADMIN_ROLE.
68
+ */
69
+ export async function setSiteBaseURI(walletClient, contractAddress, siteName, uri) {
70
+ return await walletClient.writeContract({
71
+ address: contractAddress,
72
+ abi: pasosDeJesusCredentialsAbi,
73
+ functionName: 'setSiteBaseURI',
74
+ args: [siteName, uri],
75
+ chain: walletClient.chain,
76
+ account: walletClient.account,
77
+ });
78
+ }
79
+ /**
80
+ * Set or correct a custom token URI. Only DEFAULT_ADMIN_ROLE.
81
+ */
82
+ export async function setCustomTokenURI(walletClient, contractAddress, tokenId, customURI) {
83
+ return await walletClient.writeContract({
84
+ address: contractAddress,
85
+ abi: pasosDeJesusCredentialsAbi,
86
+ functionName: 'setCustomTokenURI',
87
+ args: [BigInt(tokenId), customURI],
88
+ chain: walletClient.chain,
89
+ account: walletClient.account,
90
+ });
91
+ }
@@ -0,0 +1,33 @@
1
+ import type { Kysely } from 'kysely';
2
+ import type { PublicClient } from 'viem';
3
+ export interface CredentialMetadataRow {
4
+ token_id: number;
5
+ chain_id: string;
6
+ name: string;
7
+ type: string;
8
+ site: string;
9
+ is_premium: boolean;
10
+ is_soulbound: boolean;
11
+ image_url: string;
12
+ }
13
+ /**
14
+ * Upsert a row into credential_metadata cache.
15
+ */
16
+ export declare function upsertCredentialMetadata(db: Kysely<any>, row: CredentialMetadataRow): Promise<void>;
17
+ /**
18
+ * Get credential metadata from cache for a single token.
19
+ * Returns full CredentialMetadataRow or null if not found.
20
+ */
21
+ export declare function getCredentialMetadata(db: Kysely<any>, tokenId: number, chainId: string): Promise<CredentialMetadataRow | null>;
22
+ /**
23
+ * Sync all credential types from contract into cache (backfill).
24
+ */
25
+ export declare function syncCredentialMetadata(publicClient: PublicClient, contractAddress: `0x${string}`, db: Kysely<any>, chainId: string): Promise<number>;
26
+ /**
27
+ * Verify that the credential_metadata table has the expected English columns.
28
+ */
29
+ export declare function verifyCredentialMetadataSchema(db: Kysely<any>, tableName?: string): Promise<{
30
+ columns: number;
31
+ missing: string[];
32
+ colTypes: Record<string, string>;
33
+ } | null>;
@@ -0,0 +1,129 @@
1
+ // src/blockchain/credential-cache.ts
2
+ // Cache functions for credential_metadata table.
3
+ // Receives Kysely db instance as parameter — project-agnostic.
4
+ import pasosDeJesusCredentialsAbi from '../abis/PasosDeJesusCredentials.json' with { type: 'json' };
5
+ /**
6
+ * Upsert a row into credential_metadata cache.
7
+ */
8
+ export async function upsertCredentialMetadata(db, row) {
9
+ await db
10
+ .insertInto('credential_metadata')
11
+ .values(row)
12
+ .onConflict((oc) => oc.columns(['token_id', 'chain_id']).doUpdateSet({
13
+ name: row.name,
14
+ type: row.type,
15
+ site: row.site,
16
+ is_premium: row.is_premium,
17
+ is_soulbound: row.is_soulbound,
18
+ image_url: row.image_url,
19
+ updated_at: new Date(),
20
+ }))
21
+ .execute();
22
+ }
23
+ /**
24
+ * Get credential metadata from cache for a single token.
25
+ * Returns full CredentialMetadataRow or null if not found.
26
+ */
27
+ export async function getCredentialMetadata(db, tokenId, chainId) {
28
+ const row = await db
29
+ .selectFrom('credential_metadata')
30
+ .select([
31
+ 'token_id',
32
+ 'chain_id',
33
+ 'name',
34
+ 'type',
35
+ 'site',
36
+ 'is_premium',
37
+ 'is_soulbound',
38
+ 'image_url',
39
+ ])
40
+ .where('token_id', '=', tokenId)
41
+ .where('chain_id', '=', chainId)
42
+ .executeTakeFirst();
43
+ return row
44
+ ? {
45
+ token_id: row.token_id,
46
+ chain_id: row.chain_id,
47
+ name: row.name,
48
+ type: row.type,
49
+ site: row.site,
50
+ is_premium: row.is_premium,
51
+ is_soulbound: row.is_soulbound,
52
+ image_url: row.image_url,
53
+ }
54
+ : null;
55
+ }
56
+ /**
57
+ * Sync all credential types from contract into cache (backfill).
58
+ */
59
+ export async function syncCredentialMetadata(publicClient, contractAddress, db, chainId) {
60
+ const nextTokenId = await publicClient.readContract({
61
+ address: contractAddress,
62
+ abi: pasosDeJesusCredentialsAbi,
63
+ functionName: 'nextTokenId',
64
+ });
65
+ let count = 0;
66
+ for (let i = 1; i < Number(nextTokenId); i++) {
67
+ try {
68
+ const [name, sb, premium] = await Promise.all([
69
+ publicClient.readContract({
70
+ address: contractAddress,
71
+ abi: pasosDeJesusCredentialsAbi,
72
+ functionName: 'tokenNames',
73
+ args: [BigInt(i)],
74
+ }),
75
+ publicClient.readContract({
76
+ address: contractAddress,
77
+ abi: pasosDeJesusCredentialsAbi,
78
+ functionName: 'isSoulbound',
79
+ args: [BigInt(i)],
80
+ }),
81
+ publicClient.readContract({
82
+ address: contractAddress,
83
+ abi: pasosDeJesusCredentialsAbi,
84
+ functionName: 'isPremiumCourse',
85
+ args: [BigInt(i)],
86
+ }),
87
+ ]);
88
+ if (!name || name === '')
89
+ continue;
90
+ await upsertCredentialMetadata(db, {
91
+ token_id: i,
92
+ chain_id: chainId,
93
+ name: name,
94
+ type: 'unknown',
95
+ site: 'unknown',
96
+ is_premium: premium,
97
+ is_soulbound: sb,
98
+ image_url: `img/credential/${i}.png`,
99
+ });
100
+ count++;
101
+ }
102
+ catch {
103
+ // Skip unconfigured tokens
104
+ }
105
+ }
106
+ return count;
107
+ }
108
+ /**
109
+ * Verify that the credential_metadata table has the expected English columns.
110
+ */
111
+ export async function verifyCredentialMetadataSchema(db, tableName = 'credential_metadata') {
112
+ const { sql } = await import('kysely');
113
+ const result = await sql `
114
+ SELECT column_name, data_type
115
+ FROM information_schema.columns
116
+ WHERE table_name = ${tableName}
117
+ ORDER BY column_name
118
+ `.execute(db);
119
+ const rows = result.rows;
120
+ if (rows.length === 0)
121
+ return null; // table doesn't exist
122
+ const columns = new Set(rows.map(r => r.column_name));
123
+ const required = ['token_id', 'chain_id', 'name', 'type', 'site', 'image_url'];
124
+ const missing = required.filter(c => !columns.has(c));
125
+ const colTypes = {};
126
+ for (const r of rows)
127
+ colTypes[r.column_name] = r.data_type;
128
+ return { columns: rows.length, missing, colTypes };
129
+ }
@@ -0,0 +1,30 @@
1
+ export declare const TYPE_COLORS: Record<string, string>;
2
+ export declare const FONT_FAMILY = "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
3
+ /**
4
+ * Extract inner content of user's SVG (between <svg> tags).
5
+ */
6
+ export declare function extractSvgInner(svgText: string): string;
7
+ /**
8
+ * Validate user's SVG file before composition.
9
+ */
10
+ export declare function validateSvg(svgText: string): void;
11
+ export interface ComposeParams {
12
+ userSvgPath: string;
13
+ tokenId: number;
14
+ site: string;
15
+ siteName: string;
16
+ type: string;
17
+ isSoulbound: boolean;
18
+ isPremium: boolean;
19
+ displayName: string;
20
+ publicDir: string;
21
+ pngCommand: string;
22
+ }
23
+ /**
24
+ * Compose credential image by layering user's SVG icon with
25
+ * programmatic elements (border, site logo, lock/star overlays).
26
+ */
27
+ export declare function composeCredentialImage(params: ComposeParams): Promise<{
28
+ svgPath: string;
29
+ pngPath: string;
30
+ }>;