@stravigor/core 0.1.0 → 0.2.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 (43) hide show
  1. package/package.json +1 -1
  2. package/src/auth/auth.ts +2 -1
  3. package/src/broadcast/broadcast_manager.ts +18 -5
  4. package/src/broadcast/client.ts +10 -4
  5. package/src/cache/cache_manager.ts +6 -2
  6. package/src/cache/http_cache.ts +1 -5
  7. package/src/core/container.ts +2 -6
  8. package/src/database/database.ts +11 -7
  9. package/src/database/migration/runner.ts +3 -1
  10. package/src/database/query_builder.ts +553 -60
  11. package/src/encryption/encryption_manager.ts +7 -1
  12. package/src/exceptions/errors.ts +1 -5
  13. package/src/exceptions/http_exception.ts +4 -1
  14. package/src/generators/api_generator.ts +8 -1
  15. package/src/generators/doc_generator.ts +33 -28
  16. package/src/generators/model_generator.ts +3 -1
  17. package/src/generators/test_generator.ts +81 -91
  18. package/src/i18n/helpers.ts +5 -1
  19. package/src/i18n/i18n_manager.ts +3 -1
  20. package/src/i18n/middleware.ts +2 -8
  21. package/src/mail/helpers.ts +1 -1
  22. package/src/mail/index.ts +4 -0
  23. package/src/mail/mail_manager.ts +20 -1
  24. package/src/mail/transports/alibaba_transport.ts +88 -0
  25. package/src/mail/transports/mailgun_transport.ts +74 -0
  26. package/src/mail/transports/resend_transport.ts +3 -4
  27. package/src/mail/transports/sendgrid_transport.ts +12 -9
  28. package/src/mail/transports/smtp_transport.ts +5 -5
  29. package/src/mail/types.ts +19 -1
  30. package/src/notification/channels/discord_channel.ts +6 -1
  31. package/src/notification/channels/webhook_channel.ts +8 -3
  32. package/src/notification/helpers.ts +7 -7
  33. package/src/notification/notification_manager.ts +7 -6
  34. package/src/orm/base_model.ts +4 -2
  35. package/src/queue/queue.ts +3 -1
  36. package/src/scheduler/cron.ts +12 -6
  37. package/src/scheduler/schedule.ts +17 -8
  38. package/src/session/session_manager.ts +3 -1
  39. package/src/storage/storage_manager.ts +3 -1
  40. package/src/view/compiler.ts +1 -3
  41. package/src/view/islands/island_builder.ts +4 -4
  42. package/src/view/islands/vue_plugin.ts +11 -15
  43. package/src/view/tokenizer.ts +11 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stravigor/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "The Strav framework for Bun",
6
6
  "license": "MIT",
package/src/auth/auth.ts CHANGED
@@ -53,7 +53,8 @@ export default class Auth {
53
53
  }
54
54
 
55
55
  static get db(): Database {
56
- if (!Auth._db) throw new ConfigurationError('Auth not configured. Resolve Auth through the container first.')
56
+ if (!Auth._db)
57
+ throw new ConfigurationError('Auth not configured. Resolve Auth through the container first.')
57
58
  return Auth._db
58
59
  }
59
60
 
@@ -59,8 +59,14 @@ interface ClientConnection {
59
59
  function parsePattern(pattern: string): { regex: RegExp; paramNames: string[] } {
60
60
  const paramNames: string[] = []
61
61
  const regexStr = pattern
62
- .replace(/\/\*(\w+)/, (_, name) => { paramNames.push(name); return '/(.+)' })
63
- .replace(/:(\w+)/g, (_, name) => { paramNames.push(name); return '([^/]+)' })
62
+ .replace(/\/\*(\w+)/, (_, name) => {
63
+ paramNames.push(name)
64
+ return '/(.+)'
65
+ })
66
+ .replace(/:(\w+)/g, (_, name) => {
67
+ paramNames.push(name)
68
+ return '([^/]+)'
69
+ })
64
70
  return { regex: new RegExp(`^${regexStr}$`), paramNames }
65
71
  }
66
72
 
@@ -183,7 +189,9 @@ export default class BroadcastManager {
183
189
  BroadcastManager._pingTimer = setInterval(() => {
184
190
  const ping = JSON.stringify({ t: 'ping' })
185
191
  for (const client of BroadcastManager._clients.values()) {
186
- try { client.ws.send(ping) } catch {}
192
+ try {
193
+ client.ws.send(ping)
194
+ } catch {}
187
195
  }
188
196
  }, pingInterval)
189
197
  }
@@ -293,7 +301,10 @@ export default class BroadcastManager {
293
301
  return ctx
294
302
  }
295
303
 
296
- private static async handleSubscribe(client: ClientConnection, channelName: string): Promise<void> {
304
+ private static async handleSubscribe(
305
+ client: ClientConnection,
306
+ channelName: string
307
+ ): Promise<void> {
297
308
  if (!channelName) return
298
309
 
299
310
  // Already subscribed
@@ -389,7 +400,9 @@ export default class BroadcastManager {
389
400
  if (clientId === excludeClientId) continue
390
401
  const client = BroadcastManager._clients.get(clientId)
391
402
  if (client) {
392
- try { client.ws.send(msg) } catch {}
403
+ try {
404
+ client.ws.send(msg)
405
+ } catch {}
393
406
  }
394
407
  }
395
408
  }
@@ -74,7 +74,9 @@ export class Subscription {
74
74
  this.listeners.set(event, set)
75
75
  }
76
76
  set.add(callback)
77
- return () => { set!.delete(callback) }
77
+ return () => {
78
+ set!.delete(callback)
79
+ }
78
80
  }
79
81
 
80
82
  /**
@@ -158,7 +160,7 @@ export class Broadcast {
158
160
 
159
161
  const sub = new Subscription(
160
162
  channel,
161
- (msg) => this.send(msg),
163
+ msg => this.send(msg),
162
164
  () => this.subscriptions.delete(channel)
163
165
  )
164
166
 
@@ -190,7 +192,9 @@ export class Broadcast {
190
192
  this.listeners.set(event, set)
191
193
  }
192
194
  set.add(callback)
193
- return () => { set!.delete(callback) }
195
+ return () => {
196
+ set!.delete(callback)
197
+ }
194
198
  }
195
199
 
196
200
  /** Close the connection permanently (no reconnection). */
@@ -206,7 +210,9 @@ export class Broadcast {
206
210
  // ---------------------------------------------------------------------------
207
211
 
208
212
  private autoUrl(): string {
213
+ // @ts-ignore browser-only API
209
214
  const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'
215
+ // @ts-ignore browser-only API
210
216
  return `${proto}//${location.host}/_broadcast`
211
217
  }
212
218
 
@@ -219,7 +225,7 @@ export class Broadcast {
219
225
  this.emit('connected')
220
226
  }
221
227
 
222
- this.ws.onmessage = (event) => {
228
+ this.ws.onmessage = event => {
223
229
  try {
224
230
  const msg = JSON.parse(event.data)
225
231
  this.handleMessage(msg)
@@ -34,13 +34,17 @@ export default class CacheManager {
34
34
  if (driver === 'memory') {
35
35
  CacheManager._store = new MemoryCacheStore()
36
36
  } else {
37
- throw new ConfigurationError(`Unknown cache driver: ${driver}. Use CacheManager.useStore() for custom drivers.`)
37
+ throw new ConfigurationError(
38
+ `Unknown cache driver: ${driver}. Use CacheManager.useStore() for custom drivers.`
39
+ )
38
40
  }
39
41
  }
40
42
 
41
43
  static get store(): CacheStore {
42
44
  if (!CacheManager._store) {
43
- throw new ConfigurationError('CacheManager not configured. Resolve it through the container first.')
45
+ throw new ConfigurationError(
46
+ 'CacheManager not configured. Resolve it through the container first.'
47
+ )
44
48
  }
45
49
  return CacheManager._store
46
50
  }
@@ -97,11 +97,7 @@ export function httpCache(options: HttpCacheOptions = {}): Middleware {
97
97
  }
98
98
  }
99
99
 
100
- function buildCacheControl(
101
- directives: CacheDirective[],
102
- maxAge: number,
103
- sMaxAge?: number
104
- ): string {
100
+ function buildCacheControl(directives: CacheDirective[], maxAge: number, sMaxAge?: number): string {
105
101
  const parts = [...directives]
106
102
  if (maxAge > 0) parts.push(`max-age=${maxAge}` as CacheDirective)
107
103
  if (sMaxAge != null) parts.push(`s-maxage=${sMaxAge}` as CacheDirective)
@@ -47,9 +47,7 @@ export default class Container {
47
47
  register<T>(name: string, factory: Factory<T> | Constructor<T>): this
48
48
  register<T>(nameOrCtor: string | Constructor<T>, factory?: Factory<T> | Constructor<T>): this {
49
49
  if (typeof nameOrCtor === 'function') {
50
- const resolved = factory
51
- ? this.toFactory(factory)
52
- : this.classToFactory(nameOrCtor)
50
+ const resolved = factory ? this.toFactory(factory) : this.classToFactory(nameOrCtor)
53
51
  this.factories.set(nameOrCtor, { factory: resolved, singleton: false })
54
52
  } else {
55
53
  this.factories.set(nameOrCtor, { factory: this.toFactory(factory!), singleton: false })
@@ -65,9 +63,7 @@ export default class Container {
65
63
  singleton<T>(name: string, factory: Factory<T> | Constructor<T>): this
66
64
  singleton<T>(nameOrCtor: string | Constructor<T>, factory?: Factory<T> | Constructor<T>): this {
67
65
  if (typeof nameOrCtor === 'function') {
68
- const resolved = factory
69
- ? this.toFactory(factory)
70
- : this.classToFactory(nameOrCtor)
66
+ const resolved = factory ? this.toFactory(factory) : this.classToFactory(nameOrCtor)
71
67
  this.factories.set(nameOrCtor, { factory: resolved, singleton: true })
72
68
  } else {
73
69
  this.factories.set(nameOrCtor, { factory: this.toFactory(factory!), singleton: true })
@@ -2,12 +2,14 @@ import { SQL } from 'bun'
2
2
  import Configuration from '../config/configuration.ts'
3
3
  import { inject } from '../core/inject.ts'
4
4
  import { ConfigurationError } from '../exceptions/errors.ts'
5
+ import { env } from '../helpers/env.ts'
5
6
 
6
7
  /**
7
8
  * Database connection wrapper backed by {@link SQL Bun.sql}.
8
9
  *
9
10
  * Reads connection credentials from the `database.*` configuration keys
10
- * (loaded from `config/database.ts` / `.env`).
11
+ * (loaded from `config/database.ts` / `.env`). Falls back to reading
12
+ * `DB_*` environment variables directly when no config file is present.
11
13
  *
12
14
  * Register as a singleton in the DI container so a single connection pool
13
15
  * is shared across the application.
@@ -25,11 +27,11 @@ export default class Database {
25
27
 
26
28
  constructor(protected config: Configuration) {
27
29
  this.connection = new SQL({
28
- hostname: config.get('database.host', '127.0.0.1'),
29
- port: config.get('database.port', 5432),
30
- username: config.get('database.username', 'postgres'),
31
- password: config.get('database.password', ''),
32
- database: config.get('database.database', 'strav'),
30
+ hostname: config.get('database.host') ?? env('DB_HOST', '127.0.0.1'),
31
+ port: config.get('database.port') ?? env.int('DB_PORT', 5432),
32
+ username: config.get('database.username') ?? env('DB_USER', 'postgres'),
33
+ password: config.get('database.password') ?? env('DB_PASSWORD', ''),
34
+ database: config.get('database.database') ?? env('DB_DATABASE', 'strav'),
33
35
  })
34
36
  Database._connection = this.connection
35
37
  }
@@ -42,7 +44,9 @@ export default class Database {
42
44
  /** The global SQL connection, available after DI bootstrap. */
43
45
  static get raw(): SQL {
44
46
  if (!Database._connection) {
45
- throw new ConfigurationError('Database not configured. Resolve Database through the container first.')
47
+ throw new ConfigurationError(
48
+ 'Database not configured. Resolve Database through the container first.'
49
+ )
46
50
  }
47
51
  return Database._connection
48
52
  }
@@ -88,7 +88,9 @@ export default class MigrationRunner {
88
88
  ])
89
89
  })
90
90
  } catch (err) {
91
- throw new DatabaseError(`Migration ${version} failed: ${err instanceof Error ? err.message : err}`)
91
+ throw new DatabaseError(
92
+ `Migration ${version} failed: ${err instanceof Error ? err.message : err}`
93
+ )
92
94
  }
93
95
  }
94
96