@icarusmx/creta 0.8.2 → 0.9.1

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/bin/creta.js CHANGED
@@ -6,6 +6,7 @@ import fs from 'fs'
6
6
  import path from 'path'
7
7
  import { fileURLToPath } from 'url'
8
8
  import { CretaCodeSession } from '../lib/session.js'
9
+ import { Lesson6InterfaceDesign } from '../lessons/lesson6-interface-design.js'
9
10
 
10
11
  const ENUNCIADOS = [
11
12
  {
@@ -786,7 +787,7 @@ async function startEnunciadosSelectorInteractive() {
786
787
  return new Promise((resolve) => {
787
788
  renderOptions()
788
789
 
789
- const onKeyPress = (key) => {
790
+ const onKeyPress = async (key) => {
790
791
  if (key === 'q' || key === '\x03') { // q or Ctrl+C
791
792
  process.stdin.setRawMode(false)
792
793
  process.stdin.removeListener('data', onKeyPress)
@@ -810,13 +811,30 @@ async function startEnunciadosSelectorInteractive() {
810
811
  console.log(`\n💡 Enfoque: ${enunciadoSeleccionado.enfoque}`)
811
812
  console.log(`📚 Nivel: ${enunciadoSeleccionado.nivel}`)
812
813
 
813
- console.log("\n🚀 Próximamente:")
814
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
815
- console.log("- Ejercicios prácticos que ilustren el concepto")
816
- console.log("- Proyectos específicos para internalizar la idea")
814
+ // Check if we have a lesson implementation for this enunciado
815
+ if (enunciadoSeleccionado.id === 6) {
816
+ console.log("\n🚀 ¡Lección disponible! Iniciando sesión de estudio dirigida...")
817
+ await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
818
+
819
+ try {
820
+ const lesson6 = new Lesson6InterfaceDesign()
821
+ await lesson6.start()
822
+ } catch (error) {
823
+ console.error("\n❌ Error al ejecutar la lección:", error.message)
824
+ console.log("\n🚀 Próximamente:")
825
+ console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
826
+ console.log("- Ejercicios prácticos que ilustren el concepto")
827
+ console.log("- Proyectos específicos para internalizar la idea")
828
+ }
829
+ } else {
830
+ console.log("\n🚀 Próximamente:")
831
+ console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
832
+ console.log("- Ejercicios prácticos que ilustren el concepto")
833
+ console.log("- Proyectos específicos para internalizar la idea")
817
834
 
818
- console.log("\n💭 Por ahora, reflexiona: ¿qué parte específica de este enunciado")
819
- console.log(" te genera más curiosidad o confusión?")
835
+ console.log("\n💭 Por ahora, reflexiona: ¿qué parte específica de este enunciado")
836
+ console.log(" te genera más curiosidad o confusión?")
837
+ }
820
838
 
821
839
  resolve()
822
840
  return
@@ -876,13 +894,32 @@ async function startEnunciadosSelectorFallback() {
876
894
  console.log(`\n💡 Enfoque: ${enunciadoSeleccionado.enfoque}`)
877
895
  console.log(`📚 Nivel: ${enunciadoSeleccionado.nivel}`)
878
896
 
879
- console.log("\n🚀 Próximamente:")
880
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
881
- console.log("- Ejercicios prácticos que ilustren el concepto")
882
- console.log("- Proyectos específicos para internalizar la idea")
897
+ // Check if we have a lesson implementation for this enunciado
898
+ if (enunciadoSeleccionado.id === 6) {
899
+ console.log("\n🚀 ¡Lección disponible! Iniciando sesión de estudio dirigida...")
900
+ rl.close()
901
+ await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
902
+
903
+ try {
904
+ const lesson6 = new Lesson6InterfaceDesign()
905
+ await lesson6.start()
906
+ } catch (error) {
907
+ console.error("\n❌ Error al ejecutar la lección:", error.message)
908
+ console.log("\n🚀 Próximamente:")
909
+ console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
910
+ console.log("- Ejercicios prácticos que ilustren el concepto")
911
+ console.log("- Proyectos específicos para internalizar la idea")
912
+ }
913
+ return
914
+ } else {
915
+ console.log("\n🚀 Próximamente:")
916
+ console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
917
+ console.log("- Ejercicios prácticos que ilustren el concepto")
918
+ console.log("- Proyectos específicos para internalizar la idea")
883
919
 
884
- console.log("\n💭 Por ahora, reflexiona: ¿qué parte específica de este enunciado")
885
- console.log(" te genera más curiosidad o confusión?")
920
+ console.log("\n💭 Por ahora, reflexiona: ¿qué parte específica de este enunciado")
921
+ console.log(" te genera más curiosidad o confusión?")
922
+ }
886
923
 
887
924
  } else {
888
925
  console.log("❌ Opción no válida. Elige un número entre 1 y 6.")
@@ -0,0 +1,375 @@
1
+ #!/usr/bin/env node
2
+
3
+ // LECCIÓN 6: El énfasis al diseñar los objetos debe estar en la definición de sus solicitudes e interfaces
4
+ // Enfoque práctico: Sistema bancario con diseño interface-first y encapsulación
5
+
6
+ import { createInterface } from 'readline'
7
+
8
+ export class Lesson6InterfaceDesign {
9
+ constructor() {
10
+ this.rl = createInterface({
11
+ input: process.stdin,
12
+ output: process.stdout
13
+ })
14
+ }
15
+
16
+ async start() {
17
+ console.log("🎯 LECCIÓN 6: Diseño basado en Interfaces")
18
+ console.log("=" .repeat(50))
19
+ console.log("📚 Concepto: El énfasis al diseñar los objetos debe estar en la definición de sus solicitudes e interfaces.")
20
+ console.log("\n💡 ¿Por qué es importante?")
21
+ console.log("- Las interfaces definen QUÉ hace un objeto, no CÓMO lo hace")
22
+ console.log("- Facilita la colaboración entre objetos")
23
+ console.log("- Permite cambiar implementaciones sin afectar otros objetos")
24
+ console.log("- Hace el código más mantenible y extensible")
25
+
26
+ await this.waitForEnter("\nPresiona Enter para comenzar el ejercicio práctico...")
27
+
28
+ await this.practicalExercise()
29
+ await this.conclusion()
30
+
31
+ this.rl.close()
32
+ }
33
+
34
+ async practicalExercise() {
35
+ console.clear()
36
+ console.log("🛠️ EJERCICIO PRÁCTICO: Sistema Bancario")
37
+ console.log("=" .repeat(50))
38
+ console.log("🎯 Objetivo: Diseñar interfaces ANTES de implementar")
39
+ console.log("\nVamos a crear un sistema bancario donde:")
40
+ console.log("- BankAccount representa una cuenta con balance privado")
41
+ console.log("- Transaction registra movimientos entre cuentas")
42
+ console.log("- Bank gestiona múltiples cuentas y validaciones")
43
+
44
+ await this.waitForEnter("\nPresiona Enter para ver cómo diseñar las interfaces...")
45
+
46
+ await this.step1_DefineRequests()
47
+ await this.step2_DefineInterfaces()
48
+ await this.step3_ImplementObjects()
49
+ await this.step4_TestInteraction()
50
+ }
51
+
52
+ async step1_DefineRequests() {
53
+ console.clear()
54
+ console.log("📋 PASO 1: Definir las Solicitudes")
55
+ console.log("=" .repeat(40))
56
+ console.log("Antes de escribir código, pensemos: ¿Qué solicitudes debe manejar cada objeto?")
57
+ console.log("\n🏦 BankAccount debe poder:")
58
+ console.log(" - Consultar balance (sin exponer el dato interno)")
59
+ console.log(" - Recibir dinero (depósito)")
60
+ console.log(" - Enviar dinero (con validación interna)")
61
+ console.log(" - Proporcionar información de la cuenta")
62
+
63
+ console.log("\n📊 Transaction debe poder:")
64
+ console.log(" - Registrar origen, destino y monto")
65
+ console.log(" - Proporcionar detalles del movimiento")
66
+ console.log(" - Validar si la transacción es válida")
67
+
68
+ console.log("\n🏛️ Bank debe poder:")
69
+ console.log(" - Crear nuevas cuentas")
70
+ console.log(" - Procesar transferencias entre cuentas")
71
+ console.log(" - Validar operaciones antes de ejecutarlas")
72
+ console.log(" - Llevar registro de todas las transacciones")
73
+
74
+ await this.waitForEnter("\n✅ ¡Excelente! Ya definimos QUÉ debe hacer cada objeto. Presiona Enter para definir las interfaces...")
75
+ }
76
+
77
+ async step2_DefineInterfaces() {
78
+ console.clear()
79
+ console.log("🔌 PASO 2: Definir las Interfaces (Firmas de Operación)")
80
+ console.log("=" .repeat(50))
81
+ console.log("Ahora especifiquemos CÓMO se solicitan estas operaciones:")
82
+
83
+ console.log("\n// BankAccount Interface")
84
+ console.log("class BankAccount {")
85
+ console.log(" constructor(accountNumber, ownerName, initialBalance) → BankAccount")
86
+ console.log(" getBalance() → number // ⚠️ ENCAPSULACIÓN: acceso controlado")
87
+ console.log(" getAccountInfo() → {number, owner, balance}")
88
+ console.log(" deposit(amount) → boolean")
89
+ console.log(" send(amount, targetAccount) → boolean // ⚠️ VALIDACIÓN interna")
90
+ console.log("}")
91
+
92
+ console.log("\n// Transaction Interface")
93
+ console.log("class Transaction {")
94
+ console.log(" constructor(from, to, amount, timestamp) → Transaction")
95
+ console.log(" getDetails() → {from, to, amount, timestamp, status}")
96
+ console.log(" isValid() → boolean")
97
+ console.log("}")
98
+
99
+ console.log("\n// Bank Interface")
100
+ console.log("class Bank {")
101
+ console.log(" createAccount(ownerName, initialBalance) → BankAccount")
102
+ console.log(" transfer(fromAccount, toAccount, amount) → Transaction")
103
+ console.log(" getAllTransactions() → Array<Transaction>")
104
+ console.log(" validateOperation(account, amount) → boolean")
105
+ console.log("}")
106
+
107
+ console.log("\n💡 Observa que cada operación especifica:")
108
+ console.log(" a) Nombre de la operación (getBalance, send, transfer, etc.)")
109
+ console.log(" b) Insumos necesarios (amount, targetAccount, etc.)")
110
+ console.log(" c) Valor que regresa (boolean, Transaction, etc.)")
111
+ console.log("\n🔒 Nota especial: getBalance() encapsula el acceso al estado interno")
112
+ console.log("🛡️ Nota especial: send() incluirá validación antes de ejecutar")
113
+
114
+ await this.waitForEnter("\n✅ Interfaces definidas. Presiona Enter para implementar...")
115
+ }
116
+
117
+ async step3_ImplementObjects() {
118
+ console.clear()
119
+ console.log("⚙️ PASO 3: Implementar los Objetos")
120
+ console.log("=" .repeat(40))
121
+ console.log("Ahora que tenemos las interfaces claras, implementemos:")
122
+
123
+ console.log("\n// Implementación de BankAccount")
124
+ console.log(`class BankAccount {
125
+ constructor(accountNumber, ownerName, initialBalance) {
126
+ this.accountNumber = accountNumber
127
+ this.ownerName = ownerName
128
+ this.#balance = initialBalance // 🔒 PRIVADO: no se accede directamente
129
+ }
130
+
131
+ // 🔑 ENCAPSULACIÓN: método GET para acceder al balance
132
+ getBalance() {
133
+ return this.#balance
134
+ }
135
+
136
+ getAccountInfo() {
137
+ return {
138
+ number: this.accountNumber,
139
+ owner: this.ownerName,
140
+ balance: this.#balance // Controlamos qué se expone
141
+ }
142
+ }
143
+
144
+ deposit(amount) {
145
+ if (amount > 0) {
146
+ this.#balance += amount
147
+ return true
148
+ }
149
+ return false
150
+ }
151
+
152
+ // 🛡️ VALIDACIÓN: método SEND con lógica interna
153
+ send(amount, targetAccount) {
154
+ // Validación interna ANTES de ejecutar
155
+ if (amount > 0 && this.#balance >= amount) {
156
+ this.#balance -= amount
157
+ targetAccount.deposit(amount)
158
+ return true
159
+ }
160
+ return false // Fondos insuficientes o monto inválido
161
+ }
162
+ }`)
163
+
164
+ await this.waitForEnter("\nPresiona Enter para ver Transaction...")
165
+
166
+ console.log("\n// Implementación de Transaction")
167
+ console.log(`class Transaction {
168
+ constructor(fromAccount, toAccount, amount) {
169
+ this.fromAccount = fromAccount
170
+ this.toAccount = toAccount
171
+ this.amount = amount
172
+ this.timestamp = new Date()
173
+ this.status = 'pending'
174
+ }
175
+
176
+ getDetails() {
177
+ return {
178
+ from: this.fromAccount.accountNumber,
179
+ to: this.toAccount.accountNumber,
180
+ amount: this.amount,
181
+ timestamp: this.timestamp,
182
+ status: this.status
183
+ }
184
+ }
185
+
186
+ isValid() {
187
+ return this.amount > 0 &&
188
+ this.fromAccount.getBalance() >= this.amount
189
+ }
190
+
191
+ execute() {
192
+ if (this.isValid()) {
193
+ const success = this.fromAccount.send(this.amount, this.toAccount)
194
+ this.status = success ? 'completed' : 'failed'
195
+ return success
196
+ }
197
+ this.status = 'failed'
198
+ return false
199
+ }
200
+ }`)
201
+
202
+ await this.waitForEnter("\nPresiona Enter para ver Bank...")
203
+
204
+ console.log("\n// Implementación de Bank")
205
+ console.log(`class Bank {
206
+ constructor() {
207
+ this.accounts = []
208
+ this.transactions = []
209
+ this.nextAccountNumber = 1000
210
+ }
211
+
212
+ createAccount(ownerName, initialBalance) {
213
+ const account = new BankAccount(
214
+ this.nextAccountNumber++,
215
+ ownerName,
216
+ initialBalance
217
+ )
218
+ this.accounts.push(account)
219
+ return account
220
+ }
221
+
222
+ transfer(fromAccount, toAccount, amount) {
223
+ const transaction = new Transaction(fromAccount, toAccount, amount)
224
+
225
+ // El Bank valida ANTES de ejecutar
226
+ if (this.validateOperation(fromAccount, amount)) {
227
+ const success = transaction.execute()
228
+ this.transactions.push(transaction)
229
+ return transaction
230
+ }
231
+
232
+ transaction.status = 'failed'
233
+ this.transactions.push(transaction)
234
+ return transaction
235
+ }
236
+
237
+ validateOperation(account, amount) {
238
+ return amount > 0 && account.getBalance() >= amount
239
+ }
240
+
241
+ getAllTransactions() {
242
+ return [...this.transactions]
243
+ }
244
+ }`)
245
+
246
+ await this.waitForEnter("\n✅ Implementación completa. Presiona Enter para probar la interacción...")
247
+ }
248
+
249
+ async step4_TestInteraction() {
250
+ console.clear()
251
+ console.log("🧪 PASO 4: Probar la Interacción entre Objetos")
252
+ console.log("=" .repeat(45))
253
+ console.log("Veamos cómo los objetos colaboran usando sus interfaces:")
254
+
255
+ console.log("\n// Crear el sistema bancario")
256
+ console.log("const bank = new Bank()")
257
+ console.log("const cuenta1 = bank.createAccount('Ana García', 1000)")
258
+ console.log("const cuenta2 = bank.createAccount('Carlos López', 500)")
259
+ console.log("")
260
+ console.log("// 🔑 ENCAPSULACIÓN: Solo podemos acceder al balance via getBalance()")
261
+ console.log("console.log('Balance Ana:', cuenta1.getBalance()) // 1000")
262
+ console.log("console.log('Balance Carlos:', cuenta2.getBalance()) // 500")
263
+ console.log("")
264
+ console.log("// 🛡️ VALIDACIÓN: send() verifica internamente antes de ejecutar")
265
+ console.log("const exitoso = cuenta1.send(300, cuenta2) // ✅ Tiene fondos")
266
+ console.log("console.log('Transferencia exitosa:', exitoso) // true")
267
+ console.log("")
268
+ console.log("const fallido = cuenta1.send(1500, cuenta2) // ❌ Fondos insuficientes")
269
+ console.log("console.log('Transferencia fallida:', fallido) // false")
270
+ console.log("")
271
+ console.log("// El estado interno se mantiene íntegro")
272
+ console.log("console.log('Balance final Ana:', cuenta1.getBalance()) // 700")
273
+ console.log("console.log('Balance final Carlos:', cuenta2.getBalance()) // 800")
274
+
275
+ console.log("\n🎯 Resultado esperado:")
276
+ console.log("Balance Ana: 1000")
277
+ console.log("Balance Carlos: 500")
278
+ console.log("Transferencia exitosa: true")
279
+ console.log("Transferencia fallida: false")
280
+ console.log("Balance final Ana: 700")
281
+ console.log("Balance final Carlos: 800")
282
+
283
+ await this.waitForEnter("\nPresiona Enter para ver los beneficios de este enfoque...")
284
+
285
+ console.clear()
286
+ console.log("🌟 BENEFICIOS DEL DISEÑO INTERFACE-FIRST EN SISTEMAS BANCARIOS")
287
+ console.log("=" .repeat(60))
288
+ console.log("1. 🔒 Encapsulación: El balance está protegido, solo accesible via getBalance()")
289
+ console.log("2. 🛡️ Validación: send() incluye lógica de negocio interna")
290
+ console.log("3. 🎯 Claridad: Cada objeto tiene responsabilidades financieras bien definidas")
291
+ console.log("4. 🔗 Colaboración: Los objetos saben exactamente cómo solicitar operaciones")
292
+ console.log("5. 🧪 Testeable: Podemos simular cuentas y transacciones fácilmente")
293
+ console.log("6. 📚 Mantenible: Las interfaces documentan el contrato bancario")
294
+
295
+ console.log("\n💡 Ejemplo de extensibilidad:")
296
+ console.log("// Podemos crear diferentes tipos de cuenta sin cambiar Bank")
297
+ console.log("class SavingsAccount extends BankAccount {")
298
+ console.log(" getBalance() {")
299
+ console.log(" return super.getBalance() + this.calculateInterest()")
300
+ console.log(" }")
301
+ console.log("}")
302
+ console.log("")
303
+ console.log("class CreditAccount extends BankAccount {")
304
+ console.log(" send(amount, target) {")
305
+ console.log(" // Permite sobregiro hasta el límite de crédito")
306
+ console.log(" if (amount > 0 && (this.getBalance() + this.creditLimit) >= amount) {")
307
+ console.log(" // ... lógica específica")
308
+ console.log(" }")
309
+ console.log(" }")
310
+ console.log("}")
311
+ console.log("")
312
+ console.log("// Bank sigue funcionando igual con cualquier tipo de cuenta!")
313
+ console.log("const savingsAcc = new SavingsAccount(123, 'María', 1000)")
314
+ console.log("const creditAcc = new CreditAccount(456, 'Juan', 500, 2000) // límite crédito")
315
+ console.log("bank.transfer(savingsAcc, creditAcc, 300) // ¡Funciona!")
316
+
317
+ await this.waitForEnter("\nPresiona Enter para continuar con la conclusión...")
318
+ }
319
+
320
+ async conclusion() {
321
+ console.clear()
322
+ console.log("🎓 CONCLUSIÓN: Diseño basado en Interfaces (Sistema Bancario)")
323
+ console.log("=" .repeat(55))
324
+ console.log("🎯 Hemos demostrado que:")
325
+ console.log("\n1. El ÉNFASIS debe estar en definir QUÉ solicitudes maneja cada objeto")
326
+ console.log("2. Las INTERFACES especifican cómo hacer estas solicitudes")
327
+ console.log("3. La IMPLEMENTACIÓN viene después, siguiendo el contrato")
328
+ console.log("4. Los objetos COLABORAN a través de estas interfaces bien definidas")
329
+
330
+ console.log("\n🔑 Lecciones específicas del ejemplo bancario:")
331
+ console.log("• ENCAPSULACIÓN: getBalance() protege el acceso al estado interno")
332
+ console.log("• VALIDACIÓN: send() ejecuta lógica de negocio antes de operar")
333
+ console.log("• RESPONSABILIDAD: Cada objeto maneja su propia integridad")
334
+ console.log("• COLABORACIÓN: Bank, BankAccount y Transaction trabajan juntos")
335
+
336
+ console.log("\n📚 Conexión con lecciones anteriores:")
337
+ console.log("• Lección 2: Los objetos interactúan a través de solicitudes ✓")
338
+ console.log("• Lección 3: Las solicitudes son la única forma de ejecutar operaciones ✓")
339
+ console.log("• Lección 4: Cada operación tiene nombre, insumos y valor de retorno ✓")
340
+ console.log("• Lección 5: La interfaz es el conjunto de todas las firmas ✓")
341
+ console.log("• Lección 6: El énfasis está en definir solicitudes e interfaces ✓")
342
+
343
+ console.log("\n🚀 Próximo paso:")
344
+ console.log("Practica este enfoque en tus propios proyectos:")
345
+ console.log("1. Define primero QUÉ debe hacer cada objeto")
346
+ console.log("2. Especifica las interfaces (firmas de operación)")
347
+ console.log("3. Incluye validaciones internas en tus métodos")
348
+ console.log("4. Encapsula el estado, expón solo lo necesario")
349
+ console.log("5. Haz que los objetos colaboren a través de solicitudes")
350
+
351
+ console.log("\n💭 Reflexión final:")
352
+ console.log("En el mundo real, ¿cómo te asegurarías de que tu cuenta bancaria")
353
+ console.log("esté protegida y que solo se ejecuten operaciones válidas?")
354
+ console.log("¡Exactamente como lo hicimos aquí: interfaces bien definidas!")
355
+
356
+ console.log("\n🏆 ¡Has completado el ejemplo más importante del diseño orientado a objetos!")
357
+ console.log("Ahora entiendes por qué el ÉNFASIS debe estar en las interfaces.")
358
+
359
+ await this.waitForEnter("\n✨ ¡Lección 6 completada! Presiona Enter para salir...")
360
+ }
361
+
362
+ async waitForEnter(message) {
363
+ return new Promise((resolve) => {
364
+ this.rl.question(message, () => {
365
+ resolve()
366
+ })
367
+ })
368
+ }
369
+ }
370
+
371
+ // Para usar la lección independientemente
372
+ if (import.meta.url === `file://${process.argv[1]}`) {
373
+ const lesson = new Lesson6InterfaceDesign()
374
+ await lesson.start()
375
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icarusmx/creta",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
4
4
  "description": "Salgamos de este laberinto.",
5
5
  "type": "module",
6
6
  "bin": {