duto 0.0.0 → 0.260906.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 (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -12
  3. package/package.json +91 -2
  4. package/src/cli.ts +42 -0
  5. package/src/enum.ts +2 -0
  6. package/src/index.d.ts +61 -0
  7. package/src/index.ts +19 -0
  8. package/src/middleware.ts +2 -0
  9. package/src/pages/client/app.tsx +123 -0
  10. package/src/pages/client/hmr.ts +13 -0
  11. package/src/pages/client/island.ts +136 -0
  12. package/src/pages/client/link.ts +70 -0
  13. package/src/pages/client/router.ts +329 -0
  14. package/src/pages/client/storage.ts +95 -0
  15. package/src/pages/commands/build.ts +74 -0
  16. package/src/pages/commands/dev-old.ts +213 -0
  17. package/src/pages/commands/dev.ts +87 -0
  18. package/src/pages/commands/preview.ts +33 -0
  19. package/src/pages/compiler/aliases.ts +36 -0
  20. package/src/pages/compiler/assets.ts +43 -0
  21. package/src/pages/compiler/ast.ts +764 -0
  22. package/src/pages/compiler/constants.ts +24 -0
  23. package/src/pages/compiler/file.ts +60 -0
  24. package/src/pages/compiler/index.ts +307 -0
  25. package/src/pages/compiler/island.ts +108 -0
  26. package/src/pages/compiler/meta.ts +601 -0
  27. package/src/pages/compiler/routes.ts +29 -0
  28. package/src/pages/compiler/types.ts +209 -0
  29. package/src/pages/compiler/utils.ts +5 -0
  30. package/src/pages/compiler/vite-plugin.ts +324 -0
  31. package/src/pages/components/link.tsx +38 -0
  32. package/src/pages/i18n/client.ts +403 -0
  33. package/src/pages/i18n/element.ts +44 -0
  34. package/src/pages/i18n/server.ts +56 -0
  35. package/src/pages/index.ts +28 -0
  36. package/src/pages/preact/client-dev.ts +5 -0
  37. package/src/pages/preact/client.ts +87 -0
  38. package/src/pages/preact/context.ts +41 -0
  39. package/src/pages/preact/server.ts +186 -0
  40. package/src/pages/preact/signals.ts +89 -0
  41. package/src/pages/preact/static-html.ts +45 -0
  42. package/src/pages/preact/types.ts +26 -0
  43. package/src/server/adapter/aws.ts +4 -0
  44. package/src/server/adapter/workerd.ts +4 -0
  45. package/src/server/app.ts +96 -0
  46. package/src/server/auth/ability.ts +72 -0
  47. package/src/server/auth/authnz.ts +86 -0
  48. package/src/server/auth/index.ts +5 -0
  49. package/src/server/auth/token.ts +147 -0
  50. package/src/server/auth/types.ts +2 -0
  51. package/src/server/cli/commands/build.ts +41 -0
  52. package/src/server/cli/commands/deploy.ts +82 -0
  53. package/src/server/cli/commands/dev.ts +277 -0
  54. package/src/server/cli/commands/make.ts +98 -0
  55. package/src/server/cli/commands/migrate.ts +93 -0
  56. package/src/server/cli/commands/routes.ts +79 -0
  57. package/src/server/cli/index.ts +27 -0
  58. package/src/server/cli/stubs.ts +39 -0
  59. package/src/server/cli/types.ts +3 -0
  60. package/src/server/cli/utils.ts +443 -0
  61. package/src/server/config.ts +4 -0
  62. package/src/server/db/d1.d.ts +1 -0
  63. package/src/server/db/d1.ts +1 -0
  64. package/src/server/db/dynamodb.d.ts +1 -0
  65. package/src/server/db/dynamodb.ts +1 -0
  66. package/src/server/db/index.ts +1 -0
  67. package/src/server/entry.ts +19 -0
  68. package/src/server/handle.ts +30 -0
  69. package/src/server/http.ts +23 -0
  70. package/src/server/middlewares/auth.ts +40 -0
  71. package/src/server/middlewares/logger.ts +30 -0
  72. package/src/server/open-api/register.ts +29 -0
  73. package/src/server/open-api/spec.ts +107 -0
  74. package/src/server/request.ts +233 -0
  75. package/src/server/response.ts +142 -0
  76. package/src/server/routes.ts +474 -0
  77. package/src/server/types.ts +142 -0
  78. package/src/server/utils/environment.ts +17 -0
  79. package/src/server/utils.ts +46 -0
  80. package/src/server/validator.ts +70 -0
  81. package/src/utils.ts +5 -0
@@ -0,0 +1,147 @@
1
+ import { Envir, parseUA } from 't0n'
2
+ import { sha256, toString, type Config } from 'cripta'
3
+ import { config, create, parse } from 'cripta/token'
4
+ import type { IRequest } from '$/types'
5
+
6
+ const STABLE_HEADERS = [
7
+ 'host',
8
+ 'connection',
9
+ 'sec-ch-ua',
10
+ 'sec-ch-ua-mobile',
11
+ 'sec-ch-ua-platform',
12
+ 'upgrade-insecure-requests',
13
+ 'user-agent',
14
+ 'accept',
15
+ 'sec-fetch-site',
16
+ 'sec-fetch-mode',
17
+ 'sec-fetch-user',
18
+ 'sec-fetch-dest',
19
+ 'accept-encoding',
20
+ 'accept-language',
21
+ ]
22
+
23
+ function headerOrdering(headers: Record<string, string>) {
24
+ const order: string[] = []
25
+
26
+ for (const name in headers) {
27
+ const key = name.toLowerCase()
28
+
29
+ if (STABLE_HEADERS.includes(key))
30
+ order.push(key)
31
+ }
32
+
33
+ return order
34
+ }
35
+
36
+ export class Token {
37
+ static #cookieName: string = '__auth_'
38
+ static #name: string = 'Authorization'
39
+ static #prefix: string = 'bearer'
40
+ static #cripta: Config
41
+
42
+ static async fromRequest(req: IRequest) {
43
+ const token = this.fromCookie(req) || this.fromHeader(req)
44
+ return token ? await this.parse(req, token) : null
45
+ }
46
+
47
+ static fromHeader(req: IRequest): string | null {
48
+ const header = req.header(this.#name) || req.header('HTTP_AUTHORIZATION') || req.header('REDIRECT_HTTP_AUTHORIZATION') || null
49
+
50
+ if (header) {
51
+ const pos = header.toLowerCase().indexOf(this.#prefix.toLowerCase())
52
+ if (pos < 0) return header
53
+
54
+ let token = header.slice(pos + this.#prefix.length).trim()
55
+ const commaPos = token.indexOf(',')
56
+ if (commaPos > -1) token = token.slice(0, commaPos).trim()
57
+
58
+ return token
59
+ }
60
+
61
+ return null
62
+ }
63
+
64
+ static fromCookie(req: IRequest): string | null {
65
+ const uid = req.header('uid')
66
+
67
+ if (uid) {
68
+ const auth = req.cookie.get(this.#cookieName + uid)
69
+ return auth ? auth : null
70
+ }
71
+
72
+ return null
73
+ }
74
+
75
+ static async cripta() {
76
+ if (this.#cripta)
77
+ return this.#cripta
78
+
79
+ return this.#cripta = await config({ key: Envir.get<string>('KEY') })
80
+ }
81
+
82
+ static async parse(req: IRequest, token: string) {
83
+ const host = this.host(req)
84
+ const serveHost = Envir.get('FLOW_SERVER', host) as string
85
+
86
+ return parse(await this.cripta(), token, {
87
+ iss: serveHost,
88
+ aud: [host],
89
+ '_': await this.fingerprint(req),
90
+ })
91
+ }
92
+
93
+ static async create(req: IRequest, user: any, exp: number = 7200) {
94
+ const time = Math.floor(Date.now() / 1000)
95
+ const host = this.host(req)
96
+
97
+ return create(await this.cripta(), user, {
98
+ iss: host,
99
+ aud: [host],
100
+ iat: time,
101
+ exp: time + exp,
102
+ '_': await this.fingerprint(req),
103
+ })
104
+ }
105
+
106
+ static setPrefix(prefix: string): void {
107
+ this.#prefix = prefix
108
+ }
109
+
110
+ static setName(name: string): void {
111
+ this.#name = name
112
+ }
113
+
114
+ static host(req: IRequest): string {
115
+ const url = req.url || req.header('host')
116
+ if (!url) return ''
117
+
118
+ let formattedUrl = String(url)
119
+ if (!formattedUrl.startsWith('http'))
120
+ formattedUrl = 'http://' + formattedUrl
121
+
122
+ try {
123
+ const parsedUrl = new URL(formattedUrl)
124
+ return parsedUrl.host
125
+ } catch {
126
+ return ''
127
+ }
128
+ }
129
+
130
+ static async fingerprint(req: IRequest) {
131
+ const ua = parseUA(req.header('user-agent'))
132
+
133
+ const id = toString(await sha256(
134
+ // (req.header('accept-language') || '')
135
+ this.ip(req)
136
+ + ua.browser.name + ua.browser.version.split('.')[0]
137
+ + ua.os.type + ua.os.name
138
+ + headerOrdering(req.header()).join('')
139
+ ))
140
+
141
+ return id.substring(0, 8) + id.substring(56, 64)
142
+ }
143
+
144
+ static ip(req: IRequest) {
145
+ return req?.ip || 0
146
+ }
147
+ }
@@ -0,0 +1,2 @@
1
+ export type Roles = Record<string, string[] | '*'>
2
+ export type Abilities = string[]
@@ -0,0 +1,41 @@
1
+ import { Envir } from 't0n'
2
+ import { command } from 't0n/cli'
3
+ import { gray } from 't0n/color'
4
+ import { wait, error, rn } from 't0n/log'
5
+ import { platforms, build, normalizePlatform, platformError } from '../utils'
6
+
7
+ export default command({
8
+ meta: {
9
+ name: 'build',
10
+ description: '🗂️ Perform the build\n',
11
+ },
12
+ args: {
13
+ platform: {
14
+ alias: 'p',
15
+ description: 'Environment platform',
16
+ type: 'enum',
17
+ options: platforms,
18
+ },
19
+ minify: {
20
+ description: 'Minify the result',
21
+ type: 'boolean',
22
+ default: true,
23
+ },
24
+ },
25
+ async run({ args }) {
26
+ const platform = normalizePlatform(args.p || args.platform || Envir.get('duto.platform') || args._[0] || 'node')
27
+ if (!platform)
28
+ return platformError()
29
+
30
+ wait('Building for platform: '+ gray(platform))
31
+
32
+ try { // @ts-ignore
33
+ await build({ platform, minify: args.minify })
34
+ } catch (e: any) {
35
+ error('Build failed:\n\n', e)
36
+ process.exit(0)
37
+ } finally {
38
+ rn()
39
+ }
40
+ },
41
+ })
@@ -0,0 +1,82 @@
1
+ import { spawn } from 'node:child_process'
2
+ import { Envir } from 't0n'
3
+ import { command } from 't0n/cli'
4
+ import { error } from 't0n/log'
5
+ import { getRuntime } from 't0n/cli'
6
+
7
+ import { build, platforms, normalizePlatform, platformError } from '../utils'
8
+ import { _root } from '@/utils'
9
+
10
+ // import build from './build'
11
+
12
+ export default command({
13
+ meta: {
14
+ name: 'deploy',
15
+ description: '☁️ Perform the build and execute deploy\n',
16
+ },
17
+ args: {
18
+ platform: {
19
+ alias: 'p',
20
+ description: 'Environment platform',
21
+ type: 'enum',
22
+ options: platforms,
23
+ },
24
+ minify: {
25
+ description: 'Minify the result',
26
+ type: 'boolean',
27
+ default: true,
28
+ },
29
+ },
30
+ async run({ args }) { // @ts-ignore
31
+ const platform = normalizePlatform(args.p || args.platform || args._[0] || Envir.get('duto.platform'))
32
+ if (!platform)
33
+ return platformError()
34
+
35
+ // @ts-ignore
36
+ await build({ platform, minify: args.minify })
37
+ const isBun = getRuntime() === 'bun'
38
+
39
+ switch (platform) {
40
+ case 'aws':
41
+ // TODO: perform aws deploy
42
+ return error('Platform not yet implemented, contact the webmaster')
43
+ case 'workerd':
44
+ const child = spawn(
45
+ isBun ? 'bunx' : 'npx',
46
+ ['wrangler', 'deploy'],
47
+ {
48
+ stdio: 'inherit',
49
+ cwd: _root,
50
+ }
51
+ )
52
+
53
+ child.on('exit', code => process.exit(code ?? 0))
54
+ .on('message', msg => {
55
+ process.send && process.send(msg)
56
+ }).on('disconnect', () => {
57
+ process.disconnect && process.disconnect()
58
+ })
59
+
60
+ return
61
+ case 'vercel':
62
+ return error('Platform not yet implemented, contact the webmaster')
63
+ const vchild = spawn(
64
+ isBun ? 'bunx' : 'npx',
65
+ ['vercel', 'deploy'],
66
+ {
67
+ stdio: 'inherit',
68
+ cwd: _root,
69
+ }
70
+ )
71
+
72
+ vchild.on('exit', code => process.exit(code ?? 0))
73
+ .on('message', msg => {
74
+ process.send && process.send(msg)
75
+ }).on('disconnect', () => {
76
+ process.disconnect && process.disconnect()
77
+ })
78
+
79
+ return
80
+ }
81
+ },
82
+ })
@@ -0,0 +1,277 @@
1
+ import { join } from 'pathe'
2
+ import { spawn, type ChildProcess } from 'node:child_process'
3
+
4
+ import { command } from 't0n/cli'
5
+ import type { Miniflare } from 'miniflare'
6
+ import { WRANGLER_CONFIG_FILES, type WranglerConfig } from 'localflare-core'
7
+
8
+ import { error, event, log, rn, warn } from 't0n/log'
9
+ import { getRuntime, shutdown, watch } from 't0n/cli'
10
+
11
+ import {
12
+ build, wait, normalizePlatform, platformError,
13
+ wranglerConfig, createMiniflare, localflareManifest,
14
+ getDockerHost,
15
+ // findTsx
16
+ } from '../utils'
17
+ import { _root } from '@/utils'
18
+ import { withPort } from 't0n/port'
19
+ import { Envir } from 't0n'
20
+ import { $dev, setEnv } from '$/utils/environment'
21
+
22
+ export default command({
23
+ meta: {
24
+ name: 'dev',
25
+ description: '💻 Start the localhost server\n',
26
+ },
27
+ args: {
28
+ port: {
29
+ description: 'Port to listen on',
30
+ type: 'number',
31
+ default: 3000,
32
+ },
33
+ host: {
34
+ description: 'Host to forward requests to, defaults to the zone of project',
35
+ type: 'string',
36
+ default: 'localhost',
37
+ },
38
+ platform: {
39
+ alias: 'p',
40
+ description: 'Environment platform',
41
+ type: 'enum',
42
+ options: ['aws', 'cf', 'node'] as const,
43
+ // required: true,
44
+ },
45
+ minify: {
46
+ description: 'Minify the result',
47
+ type: 'boolean',
48
+ default: true,
49
+ },
50
+ },
51
+ async run({ args }) {
52
+ setEnv($dev)
53
+ const duto = Envir.get('duto', {})
54
+ const platform = normalizePlatform(args.p || args.platform || duto.platform || args._[0] || 'node')
55
+ if (!platform)
56
+ return platformError()
57
+
58
+ const desiredPort = args.port ? Number(args.port) : duto?.dev?.port || 3000
59
+ const host = args.host ? String(args.host) : duto?.dev?.host || 'localhost'
60
+
61
+ let isBuilding = false
62
+ const startApp = async (start: Function, stop: Function|undefined = undefined, building: boolean = true) => {
63
+ if (building) {
64
+ if (isBuilding) return
65
+ isBuilding = true
66
+ event('Building..')
67
+ }
68
+ const fn = async (silent: boolean = false) => { // @ts-ignore
69
+ building && await build({ platform, silent, minify: args.minify, env: 'dev' })
70
+ await start()
71
+ }
72
+
73
+ try {
74
+ await fn()
75
+ watch(async () => {
76
+ // event('Restarting..')
77
+ try {
78
+ await fn(true)
79
+ } catch (e) {
80
+ error(e)
81
+ }
82
+ // event('Restarted...')
83
+ }, [
84
+ join(_root, '{actions,features,routes,configs,enums,libs,locales,middlewares,models,utils}/**/*.ts'),
85
+ join(_root, 'node_modules/{duto,cripta,t0n}/**/*.ts'),
86
+ join(_root, '.env.dev'),
87
+ join(_root, '.env.prod'),
88
+ join(_root, 'package.json'),
89
+ ...WRANGLER_CONFIG_FILES.map(f => join(_root, f)),
90
+ ], _root)
91
+ // @ts-ignore
92
+ stop && shutdown(stop)
93
+ } catch (e: any) {
94
+ error(e?.stack || e?.message || e)
95
+ process.exit(0)
96
+ } finally {
97
+ isBuilding = false
98
+ }
99
+ }
100
+
101
+ const applyExit = async (app: ChildProcess | null) => {
102
+ if (!app) return null
103
+
104
+ app //?.on('exit', code => process.exit(code ?? 0))
105
+ .on('message', msg => {
106
+ process.send && process.send(msg)
107
+ }).on('disconnect', () => {
108
+ process.disconnect && process.disconnect()
109
+ })
110
+ }
111
+ const killProcess = async (app: ChildProcess | null) => {
112
+ if (!app) return null
113
+ // event('Stopping..')
114
+ try {
115
+ if (!app?.killed) {
116
+ app.kill('SIGTERM')
117
+ await wait(1000)
118
+
119
+ if (!app?.killed) { // force kill
120
+ app.kill('SIGKILL')
121
+ await wait(1000)
122
+ }
123
+ }
124
+
125
+ return null
126
+ } catch (e) {
127
+ error('Error stopping:', e)
128
+ }
129
+
130
+ return null
131
+ }
132
+
133
+ const started = (port: number) => {
134
+ log(`Starting API on http://${host}:${port}`)
135
+ if (platform === 'workerd')
136
+ log(`Localflare on https://studio.localflare.dev`)
137
+ rn()
138
+ }
139
+
140
+ withPort(desiredPort, async port => {
141
+ switch (platform) {
142
+ case 'aws':
143
+ started(port)
144
+ let lambda: ChildProcess | null = null
145
+ const stopLambda = async () => {
146
+ lambda = await killProcess(lambda)
147
+ }
148
+ const startLambda = async () => {
149
+ if (lambda) await stopLambda()
150
+
151
+ lambda = spawn(
152
+ 'sam',
153
+ [
154
+ 'local', 'start-api',
155
+ '--warm-containers', 'LAZY',
156
+ '--debug', '--template-file', join(_root, 'template-dev.yaml'),
157
+ '--port', String(port),
158
+ ],
159
+ {
160
+ stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
161
+ // stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
162
+ shell: process.platform === 'win32',
163
+ env: {...process.env, DOCKER_HOST: getDockerHost()},
164
+ }
165
+ )
166
+ //.on('exit', code => {
167
+ // warn(`Lambda process exited with code ${code ?? 0}`)
168
+ // if (code != 0 && code != null)
169
+ // error('Lambda process crashed, waiting for restart...')
170
+
171
+ // lambda = null
172
+ // }).on('message', msg => {
173
+ // process.send && process.send(msg)
174
+ // }).on('disconnect', () => {
175
+ // process.disconnect && process.disconnect()
176
+ // }).on('error', e => {
177
+ // error('Lambda process error:', e)
178
+ // lambda = null
179
+ // })
180
+ applyExit(lambda)
181
+ await wait(2000)
182
+ }
183
+
184
+ await startApp(startLambda, stopLambda)
185
+ break
186
+
187
+ case 'workerd':
188
+ started(port)
189
+
190
+ let localflare: Miniflare | null = null
191
+ const startLocalflare = async (workerConfig: WranglerConfig) => {
192
+ if (localflare) return
193
+
194
+ localflare = createMiniflare({
195
+ ...workerConfig,
196
+ vars: {
197
+ ...workerConfig.vars,
198
+ LOCALFLARE_MANIFEST: JSON.stringify(localflareManifest(workerConfig)),
199
+ },
200
+ main: '.duto/localfire.js',
201
+ port: 8788,
202
+ inspectorPort: 9230,
203
+ })
204
+
205
+ await localflare.ready
206
+ }
207
+
208
+ let worker: Miniflare | null = null
209
+ const startWorker = async () => {
210
+ if (worker) await worker.dispose()
211
+
212
+ const workerConfig = await wranglerConfig() // @ts-ignore
213
+ workerConfig.host = host // @ts-ignore
214
+ workerConfig.liveReload = false
215
+
216
+ worker = createMiniflare({ ...workerConfig, port })
217
+ await worker.ready
218
+
219
+ if (!localflare) await startLocalflare(workerConfig)
220
+ }
221
+
222
+ await startApp(startWorker)
223
+ break
224
+ default:
225
+ case 'node':
226
+ return console.error('@Deprecated')
227
+ const isBun = getRuntime() === 'bun'
228
+ const isWin32 = process.platform === 'win32'
229
+
230
+ const _arg = isBun ? 'run' : findTsx()
231
+
232
+ if (!_arg) {
233
+ console.error('Error: "tsx" is not available. Please install tsx:')
234
+ console.error(' npm i -D tsx')
235
+ console.error(' or')
236
+ console.error(' bun i -D tsx')
237
+ process.exit(1)
238
+ }
239
+
240
+ started(port)
241
+ const params = isBun
242
+ ? ['--port=' + port, '--hot', '--silent', '--no-clear-screen', '--no-summary']
243
+ : ['watch']
244
+
245
+ let nodeApp: ChildProcess | null = null
246
+ const stopNode = async () => {
247
+ nodeApp = await killProcess(nodeApp)
248
+ }
249
+
250
+ const startNode = async () => {
251
+ if (nodeApp) await stopNode()
252
+
253
+ nodeApp = spawn(
254
+ isBun && isWin32 ? 'bun' : process.execPath,
255
+ [_arg, ...params, join(_root, `node_modules/duto/src/${isBun ? 'app/dev' : 'adapter/node'}.ts`)],
256
+ {
257
+ stdio: ['inherit', isBun ? 'pipe' : 'inherit', 'inherit', 'ipc'],
258
+ env: {...process.env, PORT: port},
259
+ }
260
+ )
261
+
262
+ if (isBun && nodeApp?.stdout) {
263
+ nodeApp.stdout?.on('data', data => {
264
+ const output = data.toString()
265
+ if (!output.includes('Started development server'))
266
+ process.stdout.write(output)
267
+ })
268
+ }
269
+
270
+ applyExit(nodeApp)
271
+ }
272
+
273
+ await startApp(startNode, stopNode, false)
274
+ }
275
+ })
276
+ },
277
+ })
@@ -0,0 +1,98 @@
1
+ import { join, relative } from 'pathe'
2
+ import { command } from 't0n/cli'
3
+ import { dim } from 't0n/color'
4
+ import { event, error } from 't0n/log'
5
+ import { Migrator } from 'forj'
6
+ import { camelCase, kebabCase } from 't0n'
7
+ import { makeFile, hasExt } from '../utils'
8
+ import { _root } from '@/utils'
9
+ import * as stub from '../stubs'
10
+
11
+ export default command({
12
+ meta: {
13
+ name: 'make',
14
+ description: '📄 Create new files\n',
15
+ },
16
+ async run({ args }) {
17
+ const alias = process.argv[2]?.startsWith('make:')
18
+ const action = alias ? process.argv[2].replace('make:', '') : process.argv[3]
19
+ const name = alias ? args._[0] : args._[1]
20
+
21
+ const command = 'make'+ (alias ? ':': ' ') + action +' <name> '
22
+
23
+ if (!name)
24
+ return error('File name is required')
25
+
26
+ const makeFileName = (s: string) => {
27
+ const lastDotIndex = s.lastIndexOf('.')
28
+ const hasExtension = lastDotIndex > 0 && lastDotIndex < s.length - 1
29
+
30
+ return hasExtension
31
+ ? `${kebabCase(s.substring(0, lastDotIndex))}.${s.substring(lastDotIndex + 1)}`
32
+ : s
33
+ }
34
+
35
+ const path = (p: string, dir: string = action) => join(_root, dir + 's', p)
36
+ let fileName = ''
37
+ switch (action) {
38
+ case 'config':
39
+ fileName = path(makeFileName(name))
40
+ if (!hasExt(fileName)) fileName += '.ts'
41
+ makeFile(fileName, name.endsWith('.json') ? '{\n}' : 'export default {\n\n}\n')
42
+ break
43
+ case 'enum':
44
+ error('Action not yet implemented, contact the webmaster')
45
+ break
46
+ case 'route':
47
+ case 'action':
48
+ case 'endpoint':
49
+ fileName = path(makeFileName(name))
50
+ if (!fileName.endsWith('.ts')) fileName += '.ts'
51
+ makeFile(fileName, stub.replace(stub.route, { R_NAME: camelCase(name) }))
52
+ break
53
+ case 'migrate':
54
+ case 'migration':
55
+ const binding = alias ? args._[1] : args._[2]
56
+ if (!binding)
57
+ return error(`Provide a database: `+ dim(command +'<database>'))
58
+
59
+ fileName = Migrator.fileName(name)
60
+ const [table, create] = Migrator.guess(name)
61
+ fileName = path(join(binding || '', fileName), 'migration')
62
+ if (!fileName.endsWith('.ts')) fileName += '.ts'
63
+ makeFile(fileName, stub.replace(stub.migration, {
64
+ M_NAME: Migrator.className(name),
65
+ S_NAME: create ? 'create' : 'table',
66
+ T_NAME: table || 'TABLE_NAME',
67
+ M_CONTENT: create ? stub.migrationCreate : '//',
68
+ }))
69
+ break
70
+ case 'model':
71
+ error('Action not yet implemented, contact the webmaster')
72
+ break
73
+ case 'job':
74
+ error('Action not yet implemented, contact the webmaster')
75
+ break
76
+ case 'seed':
77
+ case 'seeder':
78
+ error('Action not yet implemented, contact the webmaster')
79
+ break
80
+ case 'test':
81
+ error('Action not yet implemented, contact the webmaster')
82
+ break
83
+ default:
84
+ return error('Invalid action: '+ dim('make <action>['+ [
85
+ 'config',
86
+ 'enum',
87
+ 'route', // 'action', 'endpoint',
88
+ 'migrate', // 'migration',
89
+ 'model',
90
+ 'job',
91
+ 'seed', // 'seeder',
92
+ 'test',
93
+ ].join('|') +'] <name>'))
94
+ }
95
+
96
+ event(action.charAt(0).toUpperCase() + action.slice(1) +' "'+ relative(_root, fileName) +'" created successfully')
97
+ },
98
+ })