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,329 @@
1
+ import { render, h } from 'preact'
2
+ // import { render, hydrate, h } from 'preact'
3
+ // import { useLocation, Switch, Route, Router } from 'wouter-preact'
4
+ // import { matchRoute } from 'wouter-preact'
5
+ // import type { Routes, IRoute } from '../types'
6
+ import type { Page, GetProps, IRoute } from '@/types'
7
+ // import { updatePickers } from '../i18n/client'
8
+
9
+ // type Route = [string, Page, IRoute['params'], GetProps]
10
+ // type Route = [string, Page] | [string, Page, IRoute['params']] | [string, Page, IRoute['params'], GetProps]
11
+ // type Routes = Route[]
12
+
13
+
14
+ // type WRouter = {
15
+ // navigate: ((href: string) => void) | null,
16
+ // routes: Set<string>,
17
+ // load: (routes: Set<string>) => WRouter,
18
+ // has: (path: string) => boolean,
19
+ // // loaded: () => void,
20
+ // mount: (routes: Routes) => void,
21
+ // // App({ container }: {
22
+ // // container: HTMLElement;
23
+ // // }): VNode<Attributes & RouterOptions & {
24
+ // // children: ComponentChild;
25
+ // // }>,
26
+ // }
27
+
28
+ // export default {
29
+ // navigate: null,
30
+ // routes: new Set<string>(),
31
+ // load(routes: Set<string>) {
32
+ // this.routes = routes
33
+ // return this
34
+ // },
35
+ // has(path: string) {
36
+ // return this.routes.has(path)
37
+ // },
38
+ // // loaded() {
39
+ // // document.body.removeAttribute('hidden')
40
+ // // const [, navigate] = useLocation()
41
+ // // this.navigate = navigate
42
+
43
+ // // },
44
+ // mount(routes: Routes) {
45
+ // // ( // If body it was server-rendered โ€” hydrate instead of render
46
+ // // document.body.childElementCount > 0
47
+ // // ? hydrate
48
+ // // : render // @ts-ignore
49
+ // // )(this.App(routes), document.body)
50
+
51
+ // // @ts-ignore
52
+ // render(this.App(routes), document.body)
53
+ // },
54
+
55
+ // App(routes: Routes, container: HTMLElement = document.body) {
56
+ // return h(Router, null,
57
+ // h(() => {
58
+ // // const __ = useRoute(location.pathname)
59
+ // // console.log(__)
60
+ // document.body.removeAttribute('hidden')
61
+ // const [, navigate] = useLocation()
62
+ // this.navigate = navigate
63
+
64
+ // // TODO: talvez registrar todos os valores pelo id do elemento em uma fila e atualizar nesse tick...
65
+ // // input, select etc registra um update val e aqui popula..
66
+ // updatePickers()
67
+ // return null
68
+ // }, null),
69
+ // h(Switch,
70
+ // null,
71
+
72
+
73
+ // // ...[
74
+ // // ['/', 'index.tsx'],
75
+ // // ['/about', 'about.tsx'],
76
+ // // ['/blog/:slug', 'blog/[slug].tsx'],
77
+ // // ['/blog/:slug?', 'blog/[[slug]].tsx'],
78
+ // // ['/shop/*', 'shop/[...slug].tsx'],
79
+ // // ['/shop/*?', 'shop/[[...slug]].tsx'],
80
+ // // ['/:authorId/:postId', '[authorId]/[postId].tsx'],
81
+ // // ['/*', '404.tsx'],
82
+ // // ['/*', 'not-found.tsx'],
83
+ // // ['/users/*', 'users/not-found.tsx'],
84
+ // // ].map(([path, name]) =>
85
+ // // h(Route, {
86
+ // // key: path,
87
+ // // path,
88
+ // // component: ({ params }: { params: Record<string, string> }) => {
89
+
90
+ // // renderRoute(container, () => h("div", { class: "p-8" }, h("h1", { class: "text-white text-xl relative top-2", style: {zIndex:'999', position:'relative', padding: '12px'} }, name), dd(params)), params)
91
+ // // // renderRoute(container, () => h("pre", { class: "p-8" }, h("h1", null, JSON.stringify(params))), params)
92
+ // // return null
93
+ // // },
94
+ // // })
95
+ // // ),
96
+
97
+
98
+ // h(Route, {
99
+ // path: this.has(location.pathname) ? location.pathname : '/',
100
+ // component: () => null,
101
+ // }),
102
+
103
+ // ...routes.map(([path, Component, defs, getProps]) =>
104
+ // h(Route, {
105
+ // // key: path,
106
+ // path,
107
+ // component: ({ params }: { params: Record<string, string> }) => {
108
+ // // Trigger async render (updates container imperatively to avoid
109
+ // // React/Preact hook rules issues with async components)
110
+
111
+ // renderRoute(container, Component, params, defs, getProps)
112
+ // // Return null โ€” renderRoute will call render() directly on the container
113
+ // return null
114
+ // },
115
+ // })
116
+ // ),
117
+
118
+ // ) )
119
+
120
+
121
+ // }
122
+ // } as WRouter
123
+
124
+ /** Captures wouter's navigate() so the duto:navigate event can call it */
125
+ // let _navigate: ((href: string) => void) | null = null
126
+
127
+ // โ”€โ”€โ”€ Page renderer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
128
+
129
+ function normalizeParams(
130
+ raw: Record<string, string>,
131
+ paramDefs: IRoute['params']
132
+ ): Record<string, string | string[] | undefined> {
133
+ const result: Record<string, string | string[] | undefined> = {};
134
+ for (const def of paramDefs) {
135
+ const val = raw[def.name];
136
+ if (def.catchAll) {
137
+ result[def.name] = val
138
+ ? val.split('/').filter(Boolean)
139
+ : def.optional ? undefined : []
140
+ } else {
141
+ result[def.name] = val === undefined && def.optional ? undefined : val;
142
+ }
143
+ }
144
+ return result
145
+ }
146
+
147
+ export async function renderRoute(
148
+ container: HTMLElement,
149
+ Component: Page,
150
+ rawParams: Record<string, string>,
151
+ paramDefs?: IRoute['params'],
152
+ getProps?: GetProps,
153
+ layouts?: Page[]
154
+ ) {
155
+ while (document.body.firstChild)
156
+ document.body.removeChild(document.body.firstChild)
157
+
158
+ // const params = normalizeParams(rawParams, paramDefs)
159
+ // const mod = await route._import()
160
+
161
+ // let props: Record<string, unknown> = { ...params }
162
+
163
+
164
+ let props: Record<string, unknown> = paramDefs ? normalizeParams(rawParams, paramDefs) : rawParams
165
+
166
+ if (typeof getProps === 'function') {
167
+ try { // TODO: melhorar tipagem do props
168
+ const fetched = await Promise.resolve(getProps(props as Record<string, string | string[] | undefined>))
169
+ props = { ...fetched, ...props }
170
+ } catch (e) {
171
+ console.error('[ssg] getProps failed:', e)
172
+ }
173
+ }
174
+
175
+ let el = h(Component, props as any)
176
+
177
+ const _layouts = layouts ? (Array.isArray(layouts) ? layouts : [layouts]) : []
178
+ for (const layout of _layouts)
179
+ el = h(layout, null, el)
180
+
181
+ render(el, container)
182
+
183
+ // TODO: melhorar tipagem do props
184
+ // render(h(Component, props as any), container)
185
+ // render(h(Component, props as any), container)
186
+ }
187
+
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+ // TODO: remover..
196
+ // function dd(objeto) {
197
+ // // Transforma o objeto em string JSON formatada com 2 espaรงos de recuo
198
+ // const json = JSON.stringify(objeto, null, 2);
199
+
200
+ // const renderizarJsonColorido = (textoJson) => {
201
+ // // Regex para capturar propriedades, strings, nรบmeros, booleanos, nulls E quebras de linha/espaรงos explicitamente
202
+ // const regex = /(\n|\r\n|\s+|"(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g;
203
+ // const partes = [];
204
+ // let ultimoIndex = 0;
205
+
206
+ // textoJson.replace(regex, (match, p1, offset) => {
207
+ // // Adiciona qualquer texto residual intermediรกrio
208
+ // if (offset > ultimoIndex) {
209
+ // partes.push(textoJson.substring(ultimoIndex, offset));
210
+ // }
211
+
212
+ // // Se capturar uma quebra de linha, insere a tag <br /> correspondente
213
+ // if (match.trim() === '' && (match.includes('\n') || match.includes('\r'))) {
214
+ // partes.push(h('br', null));
215
+
216
+ // // Se apรณs a quebra de linha houver espaรงos de indentaรงรฃo, renderiza como spans de espaรงo
217
+ // const espacos = match.replace(/[\n\r]/g, '');
218
+ // if (espacos.length > 0) {
219
+ // partes.push(h('span', { style: { whiteSpace: 'pre' } }, espacos));
220
+ // }
221
+ // }
222
+ // // Se for apenas espaรงos de recuo (sem quebra de linha)
223
+ // else if (match.trim() === '') {
224
+ // partes.push(h('span', { style: { whiteSpace: 'pre' } }, match));
225
+ // }
226
+ // // Se for um token de dado, aplica a colorizaรงรฃo do Laravel
227
+ // else {
228
+ // let estilo = { color: '#bd93f9' }; // Roxo para nรบmeros
229
+
230
+ // if (/^"/.test(match)) {
231
+ // if (/:$/.test(match)) {
232
+ // estilo = { color: '#ff79c6', fontWeight: 'bold' }; // Rosa para chaves (keys)
233
+ // } else {
234
+ // estilo = { color: '#f1fa8c' }; // Amarelo para strings
235
+ // }
236
+ // } else if (/true|false/.test(match)) {
237
+ // estilo = { color: '#8be9fd' }; // Ciano para booleanos
238
+ // } else if (/null/.test(match)) {
239
+ // estilo = { color: '#6272a4', fontStyle: 'italic' }; // Cinza para nulos
240
+ // }
241
+
242
+ // partes.push(h('span', { style: estilo }, match));
243
+ // }
244
+
245
+ // ultimoIndex = offset + match.length;
246
+ // });
247
+
248
+ // if (ultimoIndex < textoJson.length) {
249
+ // partes.push(textoJson.substring(ultimoIndex));
250
+ // }
251
+
252
+ // return partes;
253
+ // };
254
+
255
+ // // Estilo do container escuro em tela cheia
256
+ // const estiloContainer = {
257
+ // background: '#1e1e24', // Grafite escuro elegante
258
+ // color: '#f8f8f2',
259
+ // padding: '30px',
260
+ // fontFamily: '"Fira Code", "Courier New", Courier, monospace',
261
+ // fontSize: '14px',
262
+ // lineHeight: '1.6',
263
+ // overflow: 'auto',
264
+ // position: 'fixed',
265
+ // top: 0,
266
+ // left: 0,
267
+ // width: '100vw',
268
+ // height: '100vh',
269
+ // zIndex: 99,
270
+ // textAlign: 'left',
271
+ // boxSizing: 'border-box'
272
+ // };
273
+
274
+ // // Retorna a estrutura h() contendo o bloco quebrado e indentado linha por linha
275
+ // return h('div', { style: estiloContainer }, renderizarJsonColorido(json));
276
+ // }
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+ // function normalizeParams(
288
+ // raw: Record<string, string>,
289
+ // paramDefs: IRoute['params']
290
+ // ): Record<string, string | string[] | undefined> {
291
+ // const result: Record<string, string | string[] | undefined> = {};
292
+ // for (const def of paramDefs) {
293
+ // const val = raw[def.name];
294
+ // if (def.catchAll) {
295
+ // result[def.name] = val
296
+ // ? val.split('/').filter(Boolean)
297
+ // : def.optional ? undefined : []
298
+ // } else {
299
+ // result[def.name] = val === undefined && def.optional ? undefined : val;
300
+ // }
301
+ // }
302
+ // return result
303
+ // }
304
+
305
+ // async function renderRoute(
306
+ // route: IRoute,
307
+ // rawParams: Record<string, string>,
308
+ // container: HTMLElement
309
+ // ) {
310
+ // const params = normalizeParams(rawParams, route.params)
311
+ // const mod = await route._import()
312
+
313
+ // let props: Record<string, unknown> = { ...params }
314
+
315
+ // if (typeof mod.getProps === 'function') {
316
+ // try {
317
+ // const fetched = await Promise.resolve(mod.getProps(params))
318
+ // props = { ...fetched, ...params }
319
+ // } catch (e) {
320
+ // console.error('[ssg] getProps failed:', e)
321
+ // }
322
+ // } else {
323
+ // // No getProps โ†’ re-use SSG props (already in the DOM, nothing to do on first load)
324
+ // // props = { ...(window.__SSG_PROPS__ ?? {}), ...params }
325
+ // }
326
+
327
+ // render(h(mod.default, props as any), container)
328
+ // }
329
+
@@ -0,0 +1,95 @@
1
+ import { emit, on } from 't0n'
2
+
3
+ interface IStoreChange<TValue> {
4
+ key: string,
5
+ val: TValue | null,
6
+ old: TValue | null,
7
+ }
8
+
9
+ type IStoreListener<TValue> = (change: IStoreChange<TValue>) => void
10
+
11
+ /**
12
+ * Store reativo que encapsula o localStorage.
13
+ *
14
+ * - Nenhum componente toca em `window.localStorage` diretamente, sรณ nesta classe.
15
+ * - Mudanรงas feitas via `.set()`/`.remove()` disparam eventos na mesma aba.
16
+ * - Mudanรงas feitas em outras abas chegam via evento nativo `storage` e sรฃo
17
+ * normalizadas para o mesmo formato de evento.
18
+ * - Chaves e valores sรฃo tipados a partir de um schema (`TSchema`), evitando
19
+ * strings soltas e `JSON.parse`/`JSON.stringify` espalhados pelo cรณdigo.
20
+ */
21
+ class LocalStore<TSchema extends Record<string, unknown>> extends EventTarget {
22
+ #prefix: string
23
+
24
+ constructor(prefix = '') {
25
+ super()
26
+ this.#prefix = prefix
27
+
28
+ if (typeof window !== 'undefined')
29
+ window.addEventListener('storage', this.#onCrossTabChange)
30
+ }
31
+
32
+ #buildKey<K extends keyof TSchema & string>(key: K): string {
33
+ return this.#prefix ? `${this.#prefix}:${key}` : key
34
+ }
35
+
36
+ #parse<TValue>(raw: string | null): TValue | null {
37
+ if (raw === null) return null
38
+
39
+ try {
40
+ return JSON.parse(raw) as TValue
41
+ } catch {
42
+ return raw as unknown as TValue
43
+ }
44
+ }
45
+
46
+ #onCrossTabChange = (e: StorageEvent): void => {
47
+ if (!e.key) return
48
+
49
+ const hasPrefix = this.#prefix.length > 0
50
+ const expectedPrefix = `${this.#prefix}:`
51
+
52
+ if (hasPrefix && !e.key.startsWith(expectedPrefix)) return
53
+
54
+ const key = hasPrefix ? e.key.slice(expectedPrefix.length) : e.key
55
+ const val = this.#parse<TSchema[keyof TSchema]>(e.newValue)
56
+ const old = this.#parse<TSchema[keyof TSchema]>(e.oldValue)
57
+
58
+ emit(key, { key, val, old })
59
+ }
60
+
61
+ get<K extends keyof TSchema & string>(key: K): TSchema[K] | null {
62
+ if (typeof window === 'undefined') return null
63
+
64
+ return this.#parse<TSchema[K]>(window?.localStorage?.getItem(this.#buildKey(key)))
65
+ }
66
+
67
+ set<K extends keyof TSchema & string>(key: K, val: TSchema[K]): void {
68
+ if (typeof window === 'undefined') return
69
+
70
+ const old = this.get(key)
71
+ window?.localStorage?.setItem(this.#buildKey(key), JSON.stringify(val))
72
+
73
+ emit(key, { key, val, old })
74
+ }
75
+
76
+ remove<K extends keyof TSchema & string>(key: K): void {
77
+ if (typeof window === 'undefined') return
78
+
79
+ const old = this.get(key)
80
+ window?.localStorage?.removeItem(this.#buildKey(key))
81
+ emit(key, { key, val: null, old })
82
+ }
83
+
84
+ subscribe<K extends keyof TSchema & string>(
85
+ key: K,
86
+ listener: IStoreListener<TSchema[K]>,
87
+ ) {
88
+ return on(key, listener)
89
+ }
90
+ }
91
+
92
+ export const local = new LocalStore()
93
+
94
+ export { LocalStore }
95
+ export type { IStoreChange }
@@ -0,0 +1,74 @@
1
+ import { build } from 'vite'
2
+ import { filesize } from 'filesize'
3
+ import { command } from 't0n/cli'
4
+ import { dim, underline } from 't0n/color'
5
+ import { wait, error, rn, event } from 't0n/log'
6
+ import { meta } from '@/vite-plugin'
7
+ import { getConfig } from '~/utils'
8
+ import type { SummaryMetadata } from '@/types'
9
+
10
+ export default command({
11
+ meta: {
12
+ name: 'build',
13
+ description: '๐Ÿ—‚๏ธ Perform the build for production\n',
14
+ },
15
+ args: {
16
+ },
17
+ async run({ args }) {
18
+ global.__hmr = !!args?.hmr
19
+ wait('Building..')
20
+
21
+ try {
22
+ await build(await getConfig())
23
+ const st = global?.__st ? Math.ceil(performance.now() - global.__st) : 0
24
+ const metadata = meta()
25
+
26
+ const trunc = (str: string, length: number, width = 35) => length > width ? str.slice(0, width - 3) + '...' : str + (length < width ? ' '.repeat(width - length) : '')
27
+
28
+ const format = (row: SummaryMetadata, i: number, max: number, deep: boolean = false) => {
29
+ let str = (deep ? (i === max - 1 ? 'โ””' : 'โ”œ') : (i === 0 ? 'โ”Œ' : (i === max - 1 ? 'โ””' : 'โ”œ'))) + ' '
30
+
31
+ if (!deep) str += (['โ—‹', 'โ—', 'โŒพ'][row.type] || 'โ—‹') + ' '
32
+ const size = filesize(row.sizeBr || 0)
33
+ return dim(str + [row.path, size + ' '.repeat(10 - size.length) + filesize(row.size || 0)].map(col => trunc(col, col.length, deep ? 33 : 35)).join(''))
34
+ }
35
+
36
+ rn()
37
+ console.log(' '+ ['Route', 'Size'].map(col => trunc(dim(underline(col)), col.length)).join(''))
38
+
39
+ let i = 0
40
+ const max = metadata.summary.size
41
+ for (const [, row] of metadata.summary) {
42
+ console.log(format(row, i, max))
43
+
44
+ if (row.children?.size) {
45
+ let i2 = 0
46
+ const max2 = row.children.size
47
+ for (const [, r] of row.children) {
48
+ console.log(dim('โ”œ ')+ format(r, i2, max2, true))
49
+ i2++
50
+
51
+ if (i2 > 3 && max2 > 4) {
52
+ console.log(dim(` [+${max2 - i2} more paths]`))
53
+ break
54
+ }
55
+ }
56
+ }
57
+ i++
58
+ }
59
+
60
+ rn(2)
61
+ console.log(dim('โ—‹ [Static] prerendered as static HTML'))
62
+ console.log(dim('โ— [SSG] prerendered as static HTML (uses getStaticProps)'))
63
+ console.log(dim('โŒพ [CSR] rendered on client side (uses getProps)'))
64
+ rn()
65
+ event(`Builded successfully${st ? dim(` in ${st} ms`) : ''}`)
66
+ } catch (e: any) {
67
+ error(e)
68
+ // error('Build failed:', e?.message || e)
69
+ process.exit(0)
70
+ } finally {
71
+ rn()
72
+ }
73
+ },
74
+ })