@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.
- package/README.md +45 -0
- package/package.json +83 -0
- package/src/auth/access_token.ts +122 -0
- package/src/auth/auth.ts +86 -0
- package/src/auth/index.ts +7 -0
- package/src/auth/middleware/authenticate.ts +64 -0
- package/src/auth/middleware/csrf.ts +62 -0
- package/src/auth/middleware/guest.ts +46 -0
- package/src/broadcast/broadcast_manager.ts +411 -0
- package/src/broadcast/client.ts +302 -0
- package/src/broadcast/index.ts +58 -0
- package/src/cache/cache_manager.ts +56 -0
- package/src/cache/cache_store.ts +31 -0
- package/src/cache/helpers.ts +74 -0
- package/src/cache/http_cache.ts +109 -0
- package/src/cache/index.ts +6 -0
- package/src/cache/memory_store.ts +63 -0
- package/src/cli/bootstrap.ts +37 -0
- package/src/cli/commands/generate_api.ts +74 -0
- package/src/cli/commands/generate_key.ts +46 -0
- package/src/cli/commands/generate_models.ts +48 -0
- package/src/cli/commands/migration_compare.ts +152 -0
- package/src/cli/commands/migration_fresh.ts +123 -0
- package/src/cli/commands/migration_generate.ts +79 -0
- package/src/cli/commands/migration_rollback.ts +53 -0
- package/src/cli/commands/migration_run.ts +44 -0
- package/src/cli/commands/queue_flush.ts +35 -0
- package/src/cli/commands/queue_retry.ts +34 -0
- package/src/cli/commands/queue_work.ts +40 -0
- package/src/cli/commands/scheduler_work.ts +45 -0
- package/src/cli/strav.ts +33 -0
- package/src/config/configuration.ts +105 -0
- package/src/config/loaders/base_loader.ts +69 -0
- package/src/config/loaders/env_loader.ts +112 -0
- package/src/config/loaders/typescript_loader.ts +56 -0
- package/src/config/types.ts +8 -0
- package/src/core/application.ts +4 -0
- package/src/core/container.ts +117 -0
- package/src/core/index.ts +3 -0
- package/src/core/inject.ts +39 -0
- package/src/database/database.ts +54 -0
- package/src/database/index.ts +30 -0
- package/src/database/introspector.ts +446 -0
- package/src/database/migration/differ.ts +308 -0
- package/src/database/migration/file_generator.ts +125 -0
- package/src/database/migration/index.ts +18 -0
- package/src/database/migration/runner.ts +133 -0
- package/src/database/migration/sql_generator.ts +378 -0
- package/src/database/migration/tracker.ts +76 -0
- package/src/database/migration/types.ts +189 -0
- package/src/database/query_builder.ts +474 -0
- package/src/encryption/encryption_manager.ts +209 -0
- package/src/encryption/helpers.ts +158 -0
- package/src/encryption/index.ts +3 -0
- package/src/encryption/types.ts +6 -0
- package/src/events/emitter.ts +101 -0
- package/src/events/index.ts +2 -0
- package/src/exceptions/errors.ts +75 -0
- package/src/exceptions/exception_handler.ts +126 -0
- package/src/exceptions/helpers.ts +25 -0
- package/src/exceptions/http_exception.ts +129 -0
- package/src/exceptions/index.ts +23 -0
- package/src/exceptions/strav_error.ts +11 -0
- package/src/generators/api_generator.ts +972 -0
- package/src/generators/config.ts +87 -0
- package/src/generators/doc_generator.ts +974 -0
- package/src/generators/index.ts +11 -0
- package/src/generators/model_generator.ts +586 -0
- package/src/generators/route_generator.ts +188 -0
- package/src/generators/test_generator.ts +1666 -0
- package/src/helpers/crypto.ts +4 -0
- package/src/helpers/env.ts +50 -0
- package/src/helpers/identity.ts +12 -0
- package/src/helpers/index.ts +4 -0
- package/src/helpers/strings.ts +67 -0
- package/src/http/context.ts +215 -0
- package/src/http/cookie.ts +59 -0
- package/src/http/cors.ts +163 -0
- package/src/http/index.ts +16 -0
- package/src/http/middleware.ts +39 -0
- package/src/http/rate_limit.ts +173 -0
- package/src/http/router.ts +556 -0
- package/src/http/server.ts +79 -0
- package/src/i18n/defaults/en/validation.json +20 -0
- package/src/i18n/helpers.ts +72 -0
- package/src/i18n/i18n_manager.ts +155 -0
- package/src/i18n/index.ts +4 -0
- package/src/i18n/middleware.ts +90 -0
- package/src/i18n/translator.ts +96 -0
- package/src/i18n/types.ts +17 -0
- package/src/logger/index.ts +6 -0
- package/src/logger/logger.ts +100 -0
- package/src/logger/request_logger.ts +19 -0
- package/src/logger/sinks/console_sink.ts +24 -0
- package/src/logger/sinks/file_sink.ts +24 -0
- package/src/logger/sinks/sink.ts +36 -0
- package/src/mail/css_inliner.ts +79 -0
- package/src/mail/helpers.ts +212 -0
- package/src/mail/index.ts +19 -0
- package/src/mail/mail_manager.ts +92 -0
- package/src/mail/transports/log_transport.ts +69 -0
- package/src/mail/transports/resend_transport.ts +59 -0
- package/src/mail/transports/sendgrid_transport.ts +77 -0
- package/src/mail/transports/smtp_transport.ts +48 -0
- package/src/mail/types.ts +80 -0
- package/src/notification/base_notification.ts +67 -0
- package/src/notification/channels/database_channel.ts +30 -0
- package/src/notification/channels/discord_channel.ts +43 -0
- package/src/notification/channels/email_channel.ts +37 -0
- package/src/notification/channels/webhook_channel.ts +45 -0
- package/src/notification/helpers.ts +214 -0
- package/src/notification/index.ts +20 -0
- package/src/notification/notification_manager.ts +126 -0
- package/src/notification/types.ts +122 -0
- package/src/orm/base_model.ts +351 -0
- package/src/orm/decorators.ts +127 -0
- package/src/orm/index.ts +4 -0
- package/src/policy/authorize.ts +44 -0
- package/src/policy/index.ts +3 -0
- package/src/policy/policy_result.ts +13 -0
- package/src/queue/index.ts +11 -0
- package/src/queue/queue.ts +338 -0
- package/src/queue/worker.ts +197 -0
- package/src/scheduler/cron.ts +140 -0
- package/src/scheduler/index.ts +7 -0
- package/src/scheduler/runner.ts +116 -0
- package/src/scheduler/schedule.ts +183 -0
- package/src/scheduler/scheduler.ts +47 -0
- package/src/schema/database_representation.ts +122 -0
- package/src/schema/define_association.ts +60 -0
- package/src/schema/define_schema.ts +46 -0
- package/src/schema/field_builder.ts +155 -0
- package/src/schema/field_definition.ts +66 -0
- package/src/schema/index.ts +21 -0
- package/src/schema/naming.ts +19 -0
- package/src/schema/postgres.ts +109 -0
- package/src/schema/registry.ts +157 -0
- package/src/schema/representation_builder.ts +479 -0
- package/src/schema/type_builder.ts +107 -0
- package/src/schema/types.ts +35 -0
- package/src/session/index.ts +4 -0
- package/src/session/middleware.ts +46 -0
- package/src/session/session.ts +308 -0
- package/src/session/session_manager.ts +81 -0
- package/src/storage/index.ts +13 -0
- package/src/storage/local_driver.ts +46 -0
- package/src/storage/s3_driver.ts +51 -0
- package/src/storage/storage.ts +43 -0
- package/src/storage/storage_manager.ts +59 -0
- package/src/storage/types.ts +42 -0
- package/src/storage/upload.ts +91 -0
- package/src/validation/index.ts +18 -0
- package/src/validation/rules.ts +170 -0
- package/src/validation/validate.ts +41 -0
- package/src/view/cache.ts +47 -0
- package/src/view/client/islands.ts +50 -0
- package/src/view/compiler.ts +185 -0
- package/src/view/engine.ts +139 -0
- package/src/view/escape.ts +14 -0
- package/src/view/index.ts +13 -0
- package/src/view/islands/island_builder.ts +161 -0
- package/src/view/islands/vue_plugin.ts +140 -0
- package/src/view/middleware/static.ts +35 -0
- package/src/view/tokenizer.ts +172 -0
- 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
|
+
}
|