@stravigor/core 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 (165) hide show
  1. package/README.md +45 -0
  2. package/package.json +83 -0
  3. package/src/auth/access_token.ts +122 -0
  4. package/src/auth/auth.ts +86 -0
  5. package/src/auth/index.ts +7 -0
  6. package/src/auth/middleware/authenticate.ts +64 -0
  7. package/src/auth/middleware/csrf.ts +62 -0
  8. package/src/auth/middleware/guest.ts +46 -0
  9. package/src/broadcast/broadcast_manager.ts +411 -0
  10. package/src/broadcast/client.ts +302 -0
  11. package/src/broadcast/index.ts +58 -0
  12. package/src/cache/cache_manager.ts +56 -0
  13. package/src/cache/cache_store.ts +31 -0
  14. package/src/cache/helpers.ts +74 -0
  15. package/src/cache/http_cache.ts +109 -0
  16. package/src/cache/index.ts +6 -0
  17. package/src/cache/memory_store.ts +63 -0
  18. package/src/cli/bootstrap.ts +37 -0
  19. package/src/cli/commands/generate_api.ts +74 -0
  20. package/src/cli/commands/generate_key.ts +46 -0
  21. package/src/cli/commands/generate_models.ts +48 -0
  22. package/src/cli/commands/migration_compare.ts +152 -0
  23. package/src/cli/commands/migration_fresh.ts +123 -0
  24. package/src/cli/commands/migration_generate.ts +79 -0
  25. package/src/cli/commands/migration_rollback.ts +53 -0
  26. package/src/cli/commands/migration_run.ts +44 -0
  27. package/src/cli/commands/queue_flush.ts +35 -0
  28. package/src/cli/commands/queue_retry.ts +34 -0
  29. package/src/cli/commands/queue_work.ts +40 -0
  30. package/src/cli/commands/scheduler_work.ts +45 -0
  31. package/src/cli/strav.ts +33 -0
  32. package/src/config/configuration.ts +105 -0
  33. package/src/config/loaders/base_loader.ts +69 -0
  34. package/src/config/loaders/env_loader.ts +112 -0
  35. package/src/config/loaders/typescript_loader.ts +56 -0
  36. package/src/config/types.ts +8 -0
  37. package/src/core/application.ts +4 -0
  38. package/src/core/container.ts +117 -0
  39. package/src/core/index.ts +3 -0
  40. package/src/core/inject.ts +39 -0
  41. package/src/database/database.ts +54 -0
  42. package/src/database/index.ts +30 -0
  43. package/src/database/introspector.ts +446 -0
  44. package/src/database/migration/differ.ts +308 -0
  45. package/src/database/migration/file_generator.ts +125 -0
  46. package/src/database/migration/index.ts +18 -0
  47. package/src/database/migration/runner.ts +133 -0
  48. package/src/database/migration/sql_generator.ts +378 -0
  49. package/src/database/migration/tracker.ts +76 -0
  50. package/src/database/migration/types.ts +189 -0
  51. package/src/database/query_builder.ts +474 -0
  52. package/src/encryption/encryption_manager.ts +209 -0
  53. package/src/encryption/helpers.ts +158 -0
  54. package/src/encryption/index.ts +3 -0
  55. package/src/encryption/types.ts +6 -0
  56. package/src/events/emitter.ts +101 -0
  57. package/src/events/index.ts +2 -0
  58. package/src/exceptions/errors.ts +75 -0
  59. package/src/exceptions/exception_handler.ts +126 -0
  60. package/src/exceptions/helpers.ts +25 -0
  61. package/src/exceptions/http_exception.ts +129 -0
  62. package/src/exceptions/index.ts +23 -0
  63. package/src/exceptions/strav_error.ts +11 -0
  64. package/src/generators/api_generator.ts +972 -0
  65. package/src/generators/config.ts +87 -0
  66. package/src/generators/doc_generator.ts +974 -0
  67. package/src/generators/index.ts +11 -0
  68. package/src/generators/model_generator.ts +586 -0
  69. package/src/generators/route_generator.ts +188 -0
  70. package/src/generators/test_generator.ts +1666 -0
  71. package/src/helpers/crypto.ts +4 -0
  72. package/src/helpers/env.ts +50 -0
  73. package/src/helpers/identity.ts +12 -0
  74. package/src/helpers/index.ts +4 -0
  75. package/src/helpers/strings.ts +67 -0
  76. package/src/http/context.ts +215 -0
  77. package/src/http/cookie.ts +59 -0
  78. package/src/http/cors.ts +163 -0
  79. package/src/http/index.ts +16 -0
  80. package/src/http/middleware.ts +39 -0
  81. package/src/http/rate_limit.ts +173 -0
  82. package/src/http/router.ts +556 -0
  83. package/src/http/server.ts +79 -0
  84. package/src/i18n/defaults/en/validation.json +20 -0
  85. package/src/i18n/helpers.ts +72 -0
  86. package/src/i18n/i18n_manager.ts +155 -0
  87. package/src/i18n/index.ts +4 -0
  88. package/src/i18n/middleware.ts +90 -0
  89. package/src/i18n/translator.ts +96 -0
  90. package/src/i18n/types.ts +17 -0
  91. package/src/logger/index.ts +6 -0
  92. package/src/logger/logger.ts +100 -0
  93. package/src/logger/request_logger.ts +19 -0
  94. package/src/logger/sinks/console_sink.ts +24 -0
  95. package/src/logger/sinks/file_sink.ts +24 -0
  96. package/src/logger/sinks/sink.ts +36 -0
  97. package/src/mail/css_inliner.ts +79 -0
  98. package/src/mail/helpers.ts +212 -0
  99. package/src/mail/index.ts +19 -0
  100. package/src/mail/mail_manager.ts +92 -0
  101. package/src/mail/transports/log_transport.ts +69 -0
  102. package/src/mail/transports/resend_transport.ts +59 -0
  103. package/src/mail/transports/sendgrid_transport.ts +77 -0
  104. package/src/mail/transports/smtp_transport.ts +48 -0
  105. package/src/mail/types.ts +80 -0
  106. package/src/notification/base_notification.ts +67 -0
  107. package/src/notification/channels/database_channel.ts +30 -0
  108. package/src/notification/channels/discord_channel.ts +43 -0
  109. package/src/notification/channels/email_channel.ts +37 -0
  110. package/src/notification/channels/webhook_channel.ts +45 -0
  111. package/src/notification/helpers.ts +214 -0
  112. package/src/notification/index.ts +20 -0
  113. package/src/notification/notification_manager.ts +126 -0
  114. package/src/notification/types.ts +122 -0
  115. package/src/orm/base_model.ts +351 -0
  116. package/src/orm/decorators.ts +127 -0
  117. package/src/orm/index.ts +4 -0
  118. package/src/policy/authorize.ts +44 -0
  119. package/src/policy/index.ts +3 -0
  120. package/src/policy/policy_result.ts +13 -0
  121. package/src/queue/index.ts +11 -0
  122. package/src/queue/queue.ts +338 -0
  123. package/src/queue/worker.ts +197 -0
  124. package/src/scheduler/cron.ts +140 -0
  125. package/src/scheduler/index.ts +7 -0
  126. package/src/scheduler/runner.ts +116 -0
  127. package/src/scheduler/schedule.ts +183 -0
  128. package/src/scheduler/scheduler.ts +47 -0
  129. package/src/schema/database_representation.ts +122 -0
  130. package/src/schema/define_association.ts +60 -0
  131. package/src/schema/define_schema.ts +46 -0
  132. package/src/schema/field_builder.ts +155 -0
  133. package/src/schema/field_definition.ts +66 -0
  134. package/src/schema/index.ts +21 -0
  135. package/src/schema/naming.ts +19 -0
  136. package/src/schema/postgres.ts +109 -0
  137. package/src/schema/registry.ts +157 -0
  138. package/src/schema/representation_builder.ts +479 -0
  139. package/src/schema/type_builder.ts +107 -0
  140. package/src/schema/types.ts +35 -0
  141. package/src/session/index.ts +4 -0
  142. package/src/session/middleware.ts +46 -0
  143. package/src/session/session.ts +308 -0
  144. package/src/session/session_manager.ts +81 -0
  145. package/src/storage/index.ts +13 -0
  146. package/src/storage/local_driver.ts +46 -0
  147. package/src/storage/s3_driver.ts +51 -0
  148. package/src/storage/storage.ts +43 -0
  149. package/src/storage/storage_manager.ts +59 -0
  150. package/src/storage/types.ts +42 -0
  151. package/src/storage/upload.ts +91 -0
  152. package/src/validation/index.ts +18 -0
  153. package/src/validation/rules.ts +170 -0
  154. package/src/validation/validate.ts +41 -0
  155. package/src/view/cache.ts +47 -0
  156. package/src/view/client/islands.ts +50 -0
  157. package/src/view/compiler.ts +185 -0
  158. package/src/view/engine.ts +139 -0
  159. package/src/view/escape.ts +14 -0
  160. package/src/view/index.ts +13 -0
  161. package/src/view/islands/island_builder.ts +161 -0
  162. package/src/view/islands/vue_plugin.ts +140 -0
  163. package/src/view/middleware/static.ts +35 -0
  164. package/src/view/tokenizer.ts +172 -0
  165. package/tsconfig.json +4 -0
@@ -0,0 +1,188 @@
1
+ import { join } from 'node:path'
2
+ import { Archetype } from '../schema/types.ts'
3
+ import type { SchemaDefinition } from '../schema/types.ts'
4
+ import { toSnakeCase, toPascalCase, pluralize } from '../helpers/strings.ts'
5
+ import type { GeneratedFile } from './model_generator.ts'
6
+ import type { GeneratorConfig, GeneratorPaths } from './config.ts'
7
+ import { resolvePaths, relativeImport, formatAndWrite } from './config.ts'
8
+
9
+ /** Archetypes that sit under a parent's /:parentId group. */
10
+ const DEPENDENT_ARCHETYPES: Set<Archetype> = new Set([
11
+ Archetype.Component,
12
+ Archetype.Attribute,
13
+ Archetype.Event,
14
+ Archetype.Configuration,
15
+ Archetype.Contribution,
16
+ ])
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // API routing config
20
+ // ---------------------------------------------------------------------------
21
+
22
+ export enum ApiRouting {
23
+ Prefix = 'prefix',
24
+ Subdomain = 'subdomain',
25
+ }
26
+
27
+ export interface ApiRoutingConfig {
28
+ routing: ApiRouting
29
+ prefix: string
30
+ subdomain: string
31
+ }
32
+
33
+ const API_DEFAULTS: ApiRoutingConfig = {
34
+ routing: ApiRouting.Prefix,
35
+ prefix: '/api',
36
+ subdomain: 'api',
37
+ }
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // RouteGenerator
41
+ // ---------------------------------------------------------------------------
42
+
43
+ /**
44
+ * Generate a `start/api_routes.ts` file that wires all non-association
45
+ * schemas to the router using `router.resource()` — fully derived from
46
+ * archetype and parent relationships.
47
+ *
48
+ * Routing mode (prefix vs subdomain) is read from `config/http.ts`.
49
+ */
50
+ export default class RouteGenerator {
51
+ private apiConfig: ApiRoutingConfig
52
+ private paths: GeneratorPaths
53
+
54
+ constructor(
55
+ private schemas: SchemaDefinition[],
56
+ config?: GeneratorConfig,
57
+ apiConfig?: Partial<ApiRoutingConfig>
58
+ ) {
59
+ this.apiConfig = { ...API_DEFAULTS, ...apiConfig }
60
+ this.paths = resolvePaths(config)
61
+ }
62
+
63
+ /** Generate the route file contents without writing to disk. */
64
+ generate(): GeneratedFile[] {
65
+ return [this.generateRouteFile()]
66
+ }
67
+
68
+ /** Generate, format with Prettier, and write the route file to disk. */
69
+ async writeAll(): Promise<GeneratedFile[]> {
70
+ const files = this.generate()
71
+ await formatAndWrite(files)
72
+ return files
73
+ }
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // Core generation
77
+ // ---------------------------------------------------------------------------
78
+
79
+ private generateRouteFile(): GeneratedFile {
80
+ const routable = this.schemas.filter(s => s.archetype !== Archetype.Association)
81
+
82
+ // Group schemas by parent (null = root level)
83
+ const childrenOf = new Map<string | null, SchemaDefinition[]>()
84
+ for (const s of routable) {
85
+ const parent = DEPENDENT_ARCHETYPES.has(s.archetype) ? (s.parent ?? null) : null
86
+ if (!childrenOf.has(parent)) childrenOf.set(parent, [])
87
+ childrenOf.get(parent)!.push(s)
88
+ }
89
+
90
+ const lines: string[] = [
91
+ '// Generated by Strav — DO NOT EDIT',
92
+ `import { router } from '@stravigor/core/http'`,
93
+ `import { auth } from '@stravigor/core/auth'`,
94
+ '',
95
+ ]
96
+
97
+ // Controller imports (from barrel)
98
+ const controllerImport = relativeImport(this.paths.routes, this.paths.controllers)
99
+ const controllerNames = routable.map(s => toPascalCase(s.name) + 'Controller')
100
+ lines.push(`import { ${controllerNames.join(', ')} } from '${controllerImport}'`)
101
+
102
+ lines.push('')
103
+ if (this.apiConfig.routing === ApiRouting.Subdomain) {
104
+ lines.push(
105
+ `router.group({ subdomain: '${this.apiConfig.subdomain}', middleware: [auth()] }, r => {`
106
+ )
107
+ } else {
108
+ lines.push(
109
+ `router.group({ prefix: '${this.apiConfig.prefix}', middleware: [auth()] }, r => {`
110
+ )
111
+ }
112
+
113
+ // Root-level resources (entity, reference)
114
+ const roots = childrenOf.get(null) ?? []
115
+ for (const s of roots) {
116
+ const className = toPascalCase(s.name) + 'Controller'
117
+ const routePath = '/' + this.routeSegment(s.name)
118
+ lines.push(` r.resource('${routePath}', ${className})`)
119
+ }
120
+
121
+ // Parent groups — each parent with dependent children gets a group
122
+ for (const [parentName, children] of childrenOf) {
123
+ if (parentName === null) continue
124
+
125
+ const parentSegment = this.routeSegment(parentName)
126
+
127
+ lines.push('')
128
+ lines.push(` r.group({ prefix: '/${parentSegment}/:parentId' }, r => {`)
129
+
130
+ for (const child of children) {
131
+ const className = toPascalCase(child.name) + 'Controller'
132
+ const childPath = '/' + this.childSegment(child.name, parentName)
133
+
134
+ if (child.archetype === Archetype.Configuration) {
135
+ lines.push(` r.resource('${childPath}', ${className}).singleton()`)
136
+ } else {
137
+ lines.push(` r.resource('${childPath}', ${className})`)
138
+ }
139
+ }
140
+
141
+ lines.push(` })`)
142
+ }
143
+
144
+ lines.push('})')
145
+ lines.push('')
146
+
147
+ return {
148
+ path: join(this.paths.routes, 'api_routes.ts'),
149
+ content: lines.join('\n'),
150
+ }
151
+ }
152
+
153
+ // ---------------------------------------------------------------------------
154
+ // Helpers
155
+ // ---------------------------------------------------------------------------
156
+
157
+ private routeSegment(name: string): string {
158
+ return toRouteSegment(name)
159
+ }
160
+
161
+ private childSegment(childName: string, parentName: string): string {
162
+ return toChildSegment(childName, parentName)
163
+ }
164
+ }
165
+
166
+ // ---------------------------------------------------------------------------
167
+ // Exported helpers (shared with TestGenerator)
168
+ // ---------------------------------------------------------------------------
169
+
170
+ /** Pluralized, hyphenated URL segment for a schema name. */
171
+ export function toRouteSegment(name: string): string {
172
+ return pluralize(toSnakeCase(name)).replace(/_/g, '-')
173
+ }
174
+
175
+ /**
176
+ * URL segment for a dependent schema, stripping the parent prefix when the
177
+ * child name starts with the parent name (e.g. post_content → contents).
178
+ */
179
+ export function toChildSegment(childName: string, parentName: string): string {
180
+ const parentSnake = toSnakeCase(parentName)
181
+ let childSnake = toSnakeCase(childName)
182
+
183
+ if (childSnake.startsWith(parentSnake + '_')) {
184
+ childSnake = childSnake.slice(parentSnake.length + 1)
185
+ }
186
+
187
+ return pluralize(childSnake).replace(/_/g, '-')
188
+ }