@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
@@ -1,4 +1,10 @@
1
- import { hkdfSync, createCipheriv, createDecipheriv, createHmac, timingSafeEqual } from 'node:crypto'
1
+ import {
2
+ hkdfSync,
3
+ createCipheriv,
4
+ createDecipheriv,
5
+ createHmac,
6
+ timingSafeEqual,
7
+ } from 'node:crypto'
2
8
  import { inject } from '../core/inject.ts'
3
9
  import Configuration from '../config/configuration.ts'
4
10
  import type { EncryptionConfig } from './types.ts'
@@ -21,11 +21,7 @@ export class ModelNotFoundError extends StravError {
21
21
  public readonly model: string,
22
22
  public readonly id?: unknown
23
23
  ) {
24
- super(
25
- id !== undefined
26
- ? `${model} with ID ${id} not found`
27
- : `${model} not found`
28
- )
24
+ super(id !== undefined ? `${model} with ID ${id} not found` : `${model} not found`)
29
25
  }
30
26
  }
31
27
 
@@ -111,7 +111,10 @@ export class ValidationError extends HttpException {
111
111
  * throw new RateLimitError(60) // retry after 60 seconds
112
112
  */
113
113
  export class RateLimitError extends HttpException {
114
- constructor(public readonly retryAfter?: number, message?: string) {
114
+ constructor(
115
+ public readonly retryAfter?: number,
116
+ message?: string
117
+ ) {
115
118
  super(429, message)
116
119
  }
117
120
  }
@@ -34,7 +34,14 @@ const ARCHETYPE_POLICY: Record<Archetype, string[]> = {
34
34
  [Archetype.Entity]: ['canList', 'canView', 'canCreate', 'canUpdate', 'canDelete'],
35
35
  [Archetype.Attribute]: ['canList', 'canView', 'canCreate', 'canUpdate', 'canDelete'],
36
36
  [Archetype.Reference]: ['canList', 'canView', 'canCreate', 'canUpdate', 'canDelete'],
37
- [Archetype.Contribution]: ['canList', 'canView', 'canCreate', 'canUpdate', 'canDelete', 'canModerate'],
37
+ [Archetype.Contribution]: [
38
+ 'canList',
39
+ 'canView',
40
+ 'canCreate',
41
+ 'canUpdate',
42
+ 'canDelete',
43
+ 'canModerate',
44
+ ],
38
45
  [Archetype.Component]: ['canList', 'canView', 'canUpdate'],
39
46
  [Archetype.Event]: ['canList', 'canView', 'canAppend'],
40
47
  [Archetype.Configuration]: ['canView', 'canUpsert', 'canReset'],
@@ -41,14 +41,12 @@ const DEPENDENT_ARCHETYPES: Set<Archetype> = new Set([
41
41
  const SYSTEM_COLUMNS = new Set(['id', 'created_at', 'updated_at', 'deleted_at'])
42
42
 
43
43
  const ARCHETYPE_DESCRIPTIONS: Record<Archetype, string> = {
44
- [Archetype.Entity]:
45
- 'A standalone entity with full CRUD operations and soft delete support.',
44
+ [Archetype.Entity]: 'A standalone entity with full CRUD operations and soft delete support.',
46
45
  [Archetype.Contribution]:
47
46
  'A user-contributed resource under a parent entity. Full CRUD with soft delete.',
48
47
  [Archetype.Reference]:
49
48
  'A lookup/reference table with full CRUD. No soft delete &mdash; records are permanently removed.',
50
- [Archetype.Attribute]:
51
- 'A dependent attribute of a parent entity. Full CRUD with soft delete.',
49
+ [Archetype.Attribute]: 'A dependent attribute of a parent entity. Full CRUD with soft delete.',
52
50
  [Archetype.Component]:
53
51
  'A tightly-coupled component of a parent entity. Can be listed, viewed, and updated &mdash; but not independently created or deleted.',
54
52
  [Archetype.Event]:
@@ -222,19 +220,29 @@ ${content}
222
220
 
223
221
  // Introduction & Auth
224
222
  lines.push(' <div class="mb-6">')
225
- lines.push(' <p class="px-3 text-[0.6875rem] font-semibold uppercase tracking-wider text-zinc-500 mb-2">Getting Started</p>')
226
- lines.push(' <a href="#introduction" class="sidebar-link block px-3 py-1.5 text-sm text-zinc-400 rounded-md transition-colors">Introduction</a>')
227
- lines.push(' <a href="#authentication" class="sidebar-link block px-3 py-1.5 text-sm text-zinc-400 rounded-md transition-colors">Authentication</a>')
223
+ lines.push(
224
+ ' <p class="px-3 text-[0.6875rem] font-semibold uppercase tracking-wider text-zinc-500 mb-2">Getting Started</p>'
225
+ )
226
+ lines.push(
227
+ ' <a href="#introduction" class="sidebar-link block px-3 py-1.5 text-sm text-zinc-400 rounded-md transition-colors">Introduction</a>'
228
+ )
229
+ lines.push(
230
+ ' <a href="#authentication" class="sidebar-link block px-3 py-1.5 text-sm text-zinc-400 rounded-md transition-colors">Authentication</a>'
231
+ )
228
232
  lines.push(' </div>')
229
233
 
230
234
  // Resources
231
235
  lines.push(' <div>')
232
- lines.push(' <p class="px-3 text-[0.6875rem] font-semibold uppercase tracking-wider text-zinc-500 mb-2">Resources</p>')
236
+ lines.push(
237
+ ' <p class="px-3 text-[0.6875rem] font-semibold uppercase tracking-wider text-zinc-500 mb-2">Resources</p>'
238
+ )
233
239
 
234
240
  const emitSchema = (schema: SchemaDefinition): void => {
235
241
  const anchor = toSnakeCase(schema.name)
236
242
  const label = this.displayName(schema.name)
237
- lines.push(` <a href="#${anchor}" class="sidebar-link block px-3 py-1.5 text-sm text-zinc-400 rounded-md transition-colors">${label}</a>`)
243
+ lines.push(
244
+ ` <a href="#${anchor}" class="sidebar-link block px-3 py-1.5 text-sm text-zinc-400 rounded-md transition-colors">${label}</a>`
245
+ )
238
246
 
239
247
  const children = childrenOf.get(schema.name)
240
248
  if (children?.length) {
@@ -341,10 +349,7 @@ Content-Type: application/json</code></pre>
341
349
  // Per-resource section
342
350
  // ---------------------------------------------------------------------------
343
351
 
344
- private buildResourceSection(
345
- schema: SchemaDefinition,
346
- table: TableDefinition
347
- ): string {
352
+ private buildResourceSection(schema: SchemaDefinition, table: TableDefinition): string {
348
353
  const anchor = toSnakeCase(schema.name)
349
354
  const label = this.displayName(schema.name)
350
355
  const actions = ARCHETYPE_CONTROLLER[schema.archetype] ?? []
@@ -352,9 +357,7 @@ Content-Type: application/json</code></pre>
352
357
  const description = ARCHETYPE_DESCRIPTIONS[schema.archetype]
353
358
 
354
359
  const fieldsTable = this.buildFieldsTable(schema, table)
355
- const endpoints = actions
356
- .map(action => this.buildEndpoint(action, schema, table))
357
- .join('\n')
360
+ const endpoints = actions.map(action => this.buildEndpoint(action, schema, table)).join('\n')
358
361
 
359
362
  return ` <section id="${anchor}" class="mb-20">
360
363
  <div class="flex items-center gap-3">
@@ -434,11 +437,7 @@ ${rows.join('\n')}
434
437
  // Per-endpoint block
435
438
  // ---------------------------------------------------------------------------
436
439
 
437
- private buildEndpoint(
438
- action: string,
439
- schema: SchemaDefinition,
440
- table: TableDefinition
441
- ): string {
440
+ private buildEndpoint(action: string, schema: SchemaDefinition, table: TableDefinition): string {
442
441
  const { method, pathPattern, description } = this.actionMeta(action, schema)
443
442
  const methodColor = this.methodBadgeColor(method)
444
443
  const anchor = `${toSnakeCase(schema.name)}-${action}`
@@ -738,10 +737,7 @@ ${rows.join('\n')}
738
737
  }
739
738
 
740
739
  const statusCode = action === 'store' ? 201 : 200
741
- const body =
742
- action === 'index'
743
- ? JSON.stringify([obj], null, 2)
744
- : JSON.stringify(obj, null, 2)
740
+ const body = action === 'index' ? JSON.stringify([obj], null, 2) : JSON.stringify(obj, null, 2)
745
741
 
746
742
  return { statusCode, responseBody: body }
747
743
  }
@@ -769,7 +765,9 @@ ${rows.join('\n')}
769
765
  // ---------------------------------------------------------------------------
770
766
 
771
767
  private displayName(name: string): string {
772
- return toPascalCase(name).replace(/([A-Z])/g, ' $1').trim()
768
+ return toPascalCase(name)
769
+ .replace(/([A-Z])/g, ' $1')
770
+ .trim()
773
771
  }
774
772
 
775
773
  private displayNamePlural(name: string): string {
@@ -915,9 +913,16 @@ ${rows.join('\n')}
915
913
  default: {
916
914
  const snake = toSnakeCase(fieldName)
917
915
  if (snake === 'email' || snake.endsWith('_email')) return 'user@example.com'
918
- if (snake === 'url' || snake.endsWith('_url') || snake === 'website' || snake === 'homepage')
916
+ if (
917
+ snake === 'url' ||
918
+ snake.endsWith('_url') ||
919
+ snake === 'website' ||
920
+ snake === 'homepage'
921
+ )
919
922
  return 'https://example.com'
920
- const label = toPascalCase(fieldName).replace(/([A-Z])/g, ' $1').trim()
923
+ const label = toPascalCase(fieldName)
924
+ .replace(/([A-Z])/g, ' $1')
925
+ .trim()
921
926
  return `Sample ${label}`
922
927
  }
923
928
  }
@@ -272,7 +272,9 @@ export default class ModelGenerator {
272
272
  if (needsReferenceImport) decoratorImports.push('reference')
273
273
  if (needsAssociateImport) decoratorImports.push('associate')
274
274
  if (decoratorImports.length > 0) {
275
- importLines.push(`import { ${decoratorImports.join(', ')} } from '@stravigor/core/orm/decorators'`)
275
+ importLines.push(
276
+ `import { ${decoratorImports.join(', ')} } from '@stravigor/core/orm/decorators'`
277
+ )
276
278
  }
277
279
 
278
280
  for (const [entity, enumNames] of enumImports) {